|
7 | 7 | ONLY thing that selects an implementation here. |
8 | 8 |
|
9 | 9 | Each adapter encapsulates its wire (framed FC=2/FC=3, or AT-text CLDSRVHOST1 / |
10 | | -INTPARA) entirely; the parameter numbers and AT command strings live only inside |
11 | | -the implementations. Every operation returns the SAME normalized model, and an |
12 | | -operation a wire cannot confirm fails with a typed error under |
| 10 | +INTPARA / RESET) entirely; the parameter numbers and AT command strings live only |
| 11 | +inside the implementations. Every operation returns the SAME normalized model, |
| 12 | +and an operation a wire cannot confirm fails with a typed error under |
13 | 13 | ``CollectorManagementError`` -- it never simulates success. Adapters resolve the |
14 | 14 | live transport lazily through a provider callable, so a reconnect/handover never |
15 | 15 | leaves them holding a stale socket. |
|
26 | 26 |
|
27 | 27 | import asyncio |
28 | 28 | from abc import ABC, abstractmethod |
29 | | -from collections.abc import Callable, Mapping |
| 29 | +from collections.abc import Awaitable, Callable, Mapping |
30 | 30 | from dataclasses import dataclass, field |
31 | 31 |
|
32 | 32 | from ..connection.session_handle import ( |
|
51 | 51 | _AT_ENDPOINT_COMMAND = "CLDSRVHOST1" |
52 | 52 | _AT_APPLY_COMMAND = "INTPARA" |
53 | 53 | _AT_APPLY_VALUE = "29,1" |
| 54 | +_AT_REBOOT_COMMAND = "RESET" |
| 55 | +_AT_REBOOT_VALUE = "S" |
54 | 56 | # The project's confirmed AT success status (see smartess_ble.extract_at_status_code). |
55 | 57 | _AT_SUCCESS_STATUS = "W000" |
56 | 58 |
|
@@ -566,12 +568,13 @@ async def async_set_uart_baudrate(self, baudrate: str) -> str: |
566 | 568 |
|
567 | 569 |
|
568 | 570 | class AtTextCollectorManagementAdapter(CollectorManagementAdapter): |
569 | | - """Collector management over the AT-text wire (CLDSRVHOST1 / INTPARA). |
| 571 | + """Collector management over the AT-text wire. |
570 | 572 |
|
571 | | - The AT command strings live only inside this class. Endpoint read/write and |
572 | | - apply/reboot are real and confirmed by the command response. The vendor's |
573 | | - collector-restart flow uses ``AT+INTPARA=29,1``; a recovery transaction |
574 | | - separately proves the resulting disconnect and same-identity reconnect. |
| 573 | + The AT command strings live only inside this class. ``AT+INTPARA=29,1`` |
| 574 | + applies staged settings; ``AT+RESET=S`` requests a soft reboot. A ``W000`` |
| 575 | + response confirms only that the collector accepted the selected command. |
| 576 | + Recovery transactions separately prove the physical disconnect and a new |
| 577 | + same-identity session before treating a reboot as completed. |
575 | 578 | """ |
576 | 579 |
|
577 | 580 | adapter_id = ADAPTER_COLLECTOR_AT_COMMANDS |
@@ -672,6 +675,22 @@ async def _apply(self, transport: object) -> str: |
672 | 675 | ) |
673 | 676 | return status |
674 | 677 |
|
| 678 | + async def _reboot(self, transport: object) -> str: |
| 679 | + """Send RESET S and require a confirmed ``W000`` acceptance status.""" |
| 680 | + |
| 681 | + status = await self._send_write( |
| 682 | + transport, _AT_REBOOT_COMMAND, _AT_REBOOT_VALUE |
| 683 | + ) |
| 684 | + if not status: |
| 685 | + raise CollectorManagementConfirmationError( |
| 686 | + f"collector_at_reboot_unconfirmed:command={_AT_REBOOT_COMMAND}" |
| 687 | + ) |
| 688 | + if status != _AT_SUCCESS_STATUS: |
| 689 | + raise CollectorManagementCommandError( |
| 690 | + f"collector_at_reboot_rejected:command={_AT_REBOOT_COMMAND}:status={status}" |
| 691 | + ) |
| 692 | + return status |
| 693 | + |
675 | 694 | async def async_read_endpoint_state(self) -> CollectorEndpointState: |
676 | 695 | transport = self._resolve_transport() |
677 | 696 | current = await self._query(transport, _AT_ENDPOINT_COMMAND) |
@@ -716,28 +735,38 @@ async def async_write_endpoint( |
716 | 735 | extra=extra, |
717 | 736 | ) |
718 | 737 |
|
719 | | - async def _system_action(self, action: str) -> CollectorSystemActionResult: |
| 738 | + async def _system_action( |
| 739 | + self, |
| 740 | + *, |
| 741 | + action: str, |
| 742 | + operation: Callable[[object], Awaitable[str]], |
| 743 | + warning: str, |
| 744 | + ) -> CollectorSystemActionResult: |
720 | 745 | transport = self._resolve_transport(needs_write=True) |
721 | 746 | current = await self._query(transport, _AT_ENDPOINT_COMMAND) |
722 | | - await self._apply(transport) |
| 747 | + await operation(transport) |
723 | 748 | return CollectorSystemActionResult( |
724 | 749 | action=action, |
725 | 750 | current_endpoint=current, |
726 | 751 | reboot_required_before="", |
727 | 752 | performed=True, |
728 | 753 | adapter_id=self.adapter_id, |
729 | | - warnings=( |
730 | | - self._REBOOT_ACTION_WARNING |
731 | | - if action == "reboot" |
732 | | - else self._APPLY_ACTION_WARNING, |
733 | | - ), |
| 754 | + warnings=(warning,), |
734 | 755 | ) |
735 | 756 |
|
736 | 757 | async def async_apply_changes(self) -> CollectorSystemActionResult: |
737 | | - return await self._system_action("apply") |
| 758 | + return await self._system_action( |
| 759 | + action="apply", |
| 760 | + operation=self._apply, |
| 761 | + warning=self._APPLY_ACTION_WARNING, |
| 762 | + ) |
738 | 763 |
|
739 | 764 | async def async_reboot(self) -> CollectorSystemActionResult: |
740 | | - return await self._system_action("reboot") |
| 765 | + return await self._system_action( |
| 766 | + action="reboot", |
| 767 | + operation=self._reboot, |
| 768 | + warning=self._REBOOT_ACTION_WARNING, |
| 769 | + ) |
741 | 770 |
|
742 | 771 | async def async_query_parameters( |
743 | 772 | self, |
|
0 commit comments