Branch data Line data Source code
1 : : #include "node_state_machine.hpp"
2 : : #include "esp_log.h"
3 : :
4 : : /**
5 : : * State Transition Table
6 : : * ----------------------
7 : : * | Current State | Event | New State | Condition / Rationale | | :-------------- |
8 : : * :------------------------- | :------------ | :-------------------------------------------------------- | |
9 : : * UNINITIALIZED | on_init(is_hub, has_peers) | OPERATIONAL | has_peers == true | | UNINITIALIZED |
10 : : * on_init(is_hub, has_peers) | PAIRING | has_peers == false && is_hub == true | |
11 : : * UNINITIALIZED | on_init(is_hub, has_peers) | PAIRING_SCAN | has_peers == false && is_hub == false | | IDLE |
12 : : * on_pairing_requested | PAIRING | is_hub == true || has_peers == true | | IDLE |
13 : : * on_pairing_requested | PAIRING_SCAN | is_hub == false && has_peers == false | |
14 : : * OPERATIONAL | on_pairing_requested | PAIRING | is_hub == true || has_peers == true | | OPERATIONAL |
15 : : * on_pairing_requested | PAIRING_SCAN | is_hub == false && has_peers == false | |
16 : : * OPERATIONAL | on_scan_requested | RECOVERY_SCAN | Link lost, try to find channel | | PAIRING |
17 : : * on_scan_requested | RECOVERY_SCAN | Scan requested during pairing | | IDLE |
18 : : * on_scan_requested | RECOVERY_SCAN | Scan requested from idle | |
19 : : * PAIRING_SCAN | on_channel_found | PAIRING | Channel found, ready to pair | | RECOVERY_SCAN |
20 : : * on_channel_found | OPERATIONAL | Back to normal | |
21 : : * PAIRING_SCAN | on_scan_failed(has_peers) | IDLE | No HUB found. App can retry. | | RECOVERY_SCAN |
22 : : * on_scan_failed(has_peers) | IDLE | Channel lost and not rediscovered. | | PAIRING |
23 : : * on_pairing_timeout | OPERATIONAL | has_peers == true | | PAIRING |
24 : : * on_pairing_timeout | IDLE | has_peers == false |
25 : : */
26 : :
27 : : static const char* TAG = "NodeStateMachine";
28 : :
29 : 36 : NodeStateMachine::NodeStateMachine()
30 : 36 : : state_(NodeState::UNINITIALIZED)
31 : : {
32 : 36 : }
33 : :
34 : 39 : NodeState NodeStateMachine::get_state() const
35 : : {
36 : 39 : return state_.load();
37 : : }
38 : :
39 : 1 : void NodeStateMachine::reset()
40 : : {
41 : 1 : state_.store(NodeState::UNINITIALIZED);
42 : 1 : }
43 : :
44 : 32 : esp_err_t NodeStateMachine::on_init(bool is_hub, bool has_peers)
45 : : {
46 [ + + ]: 32 : if (state_.load() != NodeState::UNINITIALIZED) {
47 : : return ESP_ERR_INVALID_STATE;
48 : : }
49 [ + + ]: 31 : if (has_peers) {
50 : 10 : return transition_to(NodeState::OPERATIONAL);
51 : : }
52 : : // HUB without peers goes directly to PAIRING — already on the correct channel
53 : : // NODE without peers needs to find the HUB channel first
54 [ + + ]: 40 : return transition_to(is_hub ? NodeState::PAIRING : NodeState::PAIRING_SCAN);
55 : : }
56 : :
57 : 1 : esp_err_t NodeStateMachine::on_deinit()
58 : : {
59 : : // state_.store(NodeState::UNINITIALIZED);
60 : 1 : return transition_to(NodeState::UNINITIALIZED);
61 : : }
62 : :
63 : 9 : esp_err_t NodeStateMachine::on_pairing_requested(bool is_hub, bool has_peers)
64 : : {
65 [ + + ]: 9 : NodeState current = state_.load();
66 [ + + ]: 9 : if (current != NodeState::IDLE && current != NodeState::OPERATIONAL) {
67 : : return ESP_ERR_INVALID_STATE;
68 : : }
69 : : // HUB never scans — already on the correct channel
70 : : // NODE without peers needs to find the HUB channel first
71 [ + + ]: 5 : if (is_hub || has_peers) {
72 : 3 : return transition_to(NodeState::PAIRING);
73 : : }
74 : 2 : return transition_to(NodeState::PAIRING_SCAN);
75 : : }
76 : :
77 : 6 : esp_err_t NodeStateMachine::on_pairing_timeout(bool has_peers)
78 : : {
79 [ + + ]: 6 : if (state_.load() != NodeState::PAIRING) {
80 : : return ESP_ERR_INVALID_STATE;
81 : : }
82 : :
83 [ + + ]: 4 : return transition_to(has_peers ? NodeState::OPERATIONAL : NodeState::IDLE);
84 : : }
85 : :
86 : 9 : esp_err_t NodeStateMachine::on_scan_requested()
87 : : {
88 [ + + ]: 9 : NodeState current = state_.load();
89 [ + + ]: 9 : if (current != NodeState::OPERATIONAL && current != NodeState::PAIRING && current != NodeState::IDLE) {
90 : : return ESP_ERR_INVALID_STATE;
91 : : }
92 : 7 : return transition_to(NodeState::RECOVERY_SCAN);
93 : : }
94 : :
95 : 11 : esp_err_t NodeStateMachine::on_channel_found()
96 : : {
97 [ + + ]: 11 : NodeState current = state_.load();
98 [ + + ]: 11 : if (current == NodeState::PAIRING_SCAN) {
99 : 7 : return transition_to(NodeState::PAIRING);
100 : : }
101 [ + + ]: 4 : else if (current == NodeState::RECOVERY_SCAN) {
102 : 1 : return transition_to(NodeState::OPERATIONAL);
103 : : }
104 : :
105 : : return ESP_ERR_INVALID_STATE;
106 : : }
107 : :
108 : 12 : esp_err_t NodeStateMachine::on_scan_failed()
109 : : {
110 [ + + ]: 12 : NodeState current = state_.load();
111 : :
112 [ + + ]: 12 : if (current == NodeState::PAIRING_SCAN || current == NodeState::RECOVERY_SCAN) {
113 : 8 : return transition_to(NodeState::IDLE);
114 : : }
115 : :
116 : : return ESP_ERR_INVALID_STATE;
117 : : }
118 : :
119 : 63 : esp_err_t NodeStateMachine::transition_to(NodeState new_state)
120 : : {
121 : 63 : ESP_LOGI(TAG, "NodeState: %d -> %d", static_cast<int>(state_.load()), static_cast<int>(new_state));
122 : 63 : state_.store(new_state);
123 : 63 : return ESP_OK;
124 : : }
|