Skip to content

Commit 1a298e6

Browse files
committed
tools: ota_upgrade: UI and state handling.
Updated multiple state variables to accommodate handling multi-device state. Moved connection, copying and uploading to their own state variables. Updated table UI to use new state variables. Updated UI redraws to not depend on state change. Signed-off-by: Aeyohan Furtado <aeyohan@embeint.com>
1 parent be53123 commit 1a298e6

1 file changed

Lines changed: 95 additions & 62 deletions

File tree

src/infuse_iot/tools/ota_upgrade.py

Lines changed: 95 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from rich.progress import (
1515
DownloadColumn,
1616
Progress,
17+
TaskID,
1718
TransferSpeedColumn,
1819
)
1920
from rich.status import Status
@@ -83,6 +84,10 @@ def __init__(self, args):
8384
self._app_id = app_meta["id"]
8485
self._new_ver = app_meta["version"]
8586
self._board_crc = crc16_ccitt(app_meta["board"].encode("utf-8"))
87+
self._state_connecting: set[int] = set()
88+
self._state_copying: set[int] = set()
89+
self._state_uploading: set[int] = set()
90+
self._tasks: dict[LocalClient, TaskID] = {}
8691
self._handled: list[int] = []
8792
self._pending: dict[int, float] = {}
8893
self._missing_diffs: set[str] = set()
@@ -97,7 +102,6 @@ def __init__(self, args):
97102
DownloadColumn(),
98103
TransferSpeedColumn(),
99104
)
100-
self.task = None
101105
if args.log is None:
102106
self._log = None
103107
else:
@@ -127,6 +131,10 @@ def add_parser(cls, parser):
127131

128132
add_server_port_parser(parser)
129133

134+
@property
135+
def _actioning(self) -> set[int]:
136+
return self._state_connecting | self._state_copying | self._state_uploading
137+
130138
def progress_table(self):
131139
table = Table()
132140
table.add_column(f"{self._app_name}\n{self._new_ver}")
@@ -144,7 +152,15 @@ def progress_table(self):
144152
meta = Table(box=None)
145153
meta.add_column()
146154
meta.add_row(table)
147-
meta.add_row(Status(self.state))
155+
if self._state_connecting:
156+
meta.add_row(Status(f"Connecting to: {', '.join(f'{i:016X}' for i in self._state_connecting)}"))
157+
if self._state_copying or self._state_uploading:
158+
meta.add_row(Status(f"Writing patch file: {', '.join(
159+
f'{i:016X}' for i in self._state_copying | self._state_uploading)}")
160+
)
161+
162+
if not (self._state_connecting or self._state_copying or self._state_uploading):
163+
meta.add_row(Status(self.state))
148164
meta.add_row(self.progress)
149165

150166
return meta
@@ -153,11 +169,12 @@ def state_update(self, live: Live, state: str):
153169
self.state = state
154170
live.update(self.progress_table())
155171

156-
def data_progress_cb(self, offset):
157-
if self.task is None:
158-
self.state = "Writing patch file"
159-
self.task = self.progress.add_task("", total=len(self.patch_file))
160-
self.progress.update(self.task, completed=offset)
172+
def data_progress_cb(self, offset, client: LocalClient):
173+
task = self._tasks.get(client)
174+
if task is None:
175+
task = self.progress.add_task("", total=len(self.patch_file))
176+
self._tasks[client] = task
177+
self.progress.update(task, completed=offset)
161178

162179
def gateway_diff_load(self, client: LocalClient):
163180
assert self._single_diff is not None
@@ -183,57 +200,68 @@ def gateway_diff_load(self, client: LocalClient):
183200
print(f"'{self._single_diff}' written to gateway")
184201

185202
def run_file_upload(self, live: Live, mtu: int, source: HopReceived, client: LocalClient):
186-
self.state_update(live, f"Uploading patch file to {source.infuse_id:016X}")
187-
rpc_client = RpcClient(client, mtu, source.infuse_id)
188-
189-
params = file_write_basic.request(rpc_enum_file_action.APP_CPATCH, binascii.crc32(self.patch_file))
190-
191-
hdr, _rsp = rpc_client.run_data_send_cmd(
192-
file_write_basic.COMMAND_ID,
193-
Auth.DEVICE,
194-
bytes(params),
195-
self.patch_file,
196-
self.data_progress_cb,
197-
file_write_basic.response.from_buffer_copy,
198-
)
203+
try:
204+
self._state_uploading.add(source.infuse_id)
205+
live.update(self.progress_table())
206+
# self.state_update(live, f"Uploading patch file to {source.infuse_id:016X}")
207+
rpc_client = RpcClient(client, mtu, source.infuse_id)
208+
209+
params = file_write_basic.request(rpc_enum_file_action.APP_CPATCH, binascii.crc32(self.patch_file))
199210

200-
if hdr is None:
201-
self._failed += 1
202-
elif hdr.return_code == 0:
203-
self._pending[source.infuse_id] = time.time() + 60
211+
hdr, _rsp = rpc_client.run_data_send_cmd(
212+
file_write_basic.COMMAND_ID,
213+
Auth.DEVICE,
214+
bytes(params),
215+
self.patch_file,
216+
lambda offset: self.data_progress_cb(offset, client),
217+
file_write_basic.response.from_buffer_copy,
218+
)
219+
220+
if hdr is None:
221+
self._failed += 1
222+
elif hdr.return_code == 0:
223+
self._pending[source.infuse_id] = time.time() + 60
224+
finally:
225+
self._state_uploading.remove(source.infuse_id)
226+
live.update(self.progress_table())
204227

205228
def run_file_copy(self, live: Live, mtu: int, source: HopReceived, client: LocalClient):
206-
self.state_update(live, f"Copying patch file to {source.infuse_id:016X}")
207-
rpc_client = RpcClient(client, mtu, InfuseID.GATEWAY)
208-
209-
params = bt_file_copy_basic.request(
210-
source.interface_address.val.to_rpc_struct(),
211-
rpc_enum_file_action.APP_CPATCH,
212-
0,
213-
len(self.patch_file),
214-
binascii.crc32(self.patch_file),
215-
1,
216-
3,
217-
)
229+
try:
230+
self._state_uploading.add(source.infuse_id)
231+
live.update(self.progress_table())
232+
rpc_client = RpcClient(client, mtu, InfuseID.GATEWAY)
233+
234+
params = bt_file_copy_basic.request(
235+
source.interface_address.val.to_rpc_struct(),
236+
rpc_enum_file_action.APP_CPATCH,
237+
0,
238+
len(self.patch_file),
239+
binascii.crc32(self.patch_file),
240+
1,
241+
3,
242+
)
218243

219-
hdr, _rsp = rpc_client.run_standard_cmd(
220-
bt_file_copy_basic.COMMAND_ID,
221-
Auth.DEVICE,
222-
bytes(params),
223-
bt_file_copy_basic.response.from_buffer_copy,
224-
)
225-
if hdr is None:
226-
self._failed += 1
227-
elif hdr.return_code == 0:
228-
self._pending[source.infuse_id] = time.time() + 60
229-
elif hdr.return_code < 0:
230-
sock_name = client._input_sock.getsockname()
231-
err = errno.strerror(-hdr.return_code)
232-
print(f"{sock_name} Failed to copy patch file to {source.infuse_id:016X} ({err})")
244+
hdr, _rsp = rpc_client.run_standard_cmd(
245+
bt_file_copy_basic.COMMAND_ID,
246+
Auth.DEVICE,
247+
bytes(params),
248+
bt_file_copy_basic.response.from_buffer_copy,
249+
)
250+
if hdr is None:
251+
self._failed += 1
252+
elif hdr.return_code == 0:
253+
self._pending[source.infuse_id] = time.time() + 60
254+
elif hdr.return_code < 0:
255+
sock_name = client._input_sock.getsockname()
256+
err = errno.strerror(-hdr.return_code)
257+
print(f"{sock_name} Failed to copy patch file to {source.infuse_id:016X} ({err})")
258+
finally:
259+
self._state_uploading.remove(source.infuse_id)
260+
live.update(self.progress_table())
233261

234262
def run_thread(self, live: Live, client: LocalClient):
235263
for source, announce in client.observe_announce():
236-
self.state_update(live, "Scanning")
264+
live.update(self.progress_table())
237265
if len(self._explicit_ids):
238266
if source.infuse_id not in self._explicit_ids:
239267
continue
@@ -275,7 +303,7 @@ def run_thread(self, live: Live, client: LocalClient):
275303
if v_str == self._new_ver and announce.application == self._app_id:
276304
self._handled.append(source.infuse_id)
277305
self._already += 1
278-
self.state_update(live, "Scanning")
306+
live.update(self.progress_table())
279307
if self._log:
280308
self._log.write(
281309
f"{time.time()},0x{source.infuse_id:016x},0x{self._app_id:08x},{v_str},already\n"
@@ -293,15 +321,15 @@ def run_thread(self, live: Live, client: LocalClient):
293321
self._missing_diffs.add(v_str)
294322
self._handled.append(source.infuse_id)
295323
self._no_diff += 1
296-
self.state_update(live, "Scanning")
324+
live.update(self.progress_table())
297325
continue
298326

299327
if self._single_diff and self._single_diff != diff_file:
300328
# Not the file we've copied to the gateway flash
301329
self._missing_diffs.add(v_str)
302330
self._handled.append(source.infuse_id)
303331
self._no_diff += 1
304-
self.state_update(live, "Scanning")
332+
live.update(self.progress_table())
305333
continue
306334

307335
# Is signal strong enough to connect?
@@ -312,27 +340,32 @@ def run_thread(self, live: Live, client: LocalClient):
312340
with open(diff_file, "rb") as f:
313341
self.patch_file = f.read()
314342

343+
if source.infuse_id in self._actioning:
344+
continue
345+
315346
# Attempt to upload
316-
self.state_update(live, f"Connecting to {source.infuse_id:016X}")
347+
self._state_connecting.add(source.infuse_id)
348+
live.update(self.progress_table())
317349
try:
318350
with client.connection(
319351
source.infuse_id, GatewayRequestConnectionRequest.DataType.COMMAND, self._conn_timeout
320352
) as mtu:
353+
self._state_connecting.remove(source.infuse_id)
321354
if self._single_diff:
322355
self.run_file_copy(live, mtu, source, client)
323356
else:
324357
self.run_file_upload(live, mtu, source, client)
325358

326359
except ConnectionRefusedError:
327-
self.state_update(live, "Scanning")
360+
self._state_connecting.remove(source.infuse_id)
328361
except ConnectionAbortedError:
329-
self.state_update(live, "Scanning")
330-
331-
if self.task is not None:
332-
self.progress.remove_task(self.task)
333-
self.task = None
362+
if source.infuse_id in self._state_connecting:
363+
self._state_connecting.remove(source.infuse_id)
334364

335-
self.state_update(live, "Scanning")
365+
if client in self._tasks:
366+
self.progress.remove_task(self._tasks[client])
367+
del self._tasks[client]
368+
live.update(self.progress_table())
336369

337370
def run(self):
338371
if not self._client.comms_check():

0 commit comments

Comments
 (0)