3
3
//! the system is in unlimited power state. In this mode [provider_unlimited](super::Config::provider_unlimited)
4
4
//! is provided to each device. Above this threshold, the system is in limited power state.
5
5
//! In this mode [provider_limited](super::Config::provider_limited) is provided to each device
6
- use embedded_services:: { debug, trace} ;
6
+ //! Lastly, the system can be in recovery mode. This mode is only entered when a connected provider fails to
7
+ //! connect at a new power level. In this mode, all connected providers are set to
8
+ //! [provider_recovery](super::Config::provider_recovery). While in this mode
9
+ //! [attempt_provider_recovery](PowerPolicy::attempt_provider_recovery) is called periodically
10
+ //! which attempts to disconnect all providers in recovery mode. If this succeeds, the system will
11
+ //! return to normal operating mode.
12
+ use embedded_services:: { debug, trace, warn} ;
7
13
8
14
use super :: * ;
9
15
@@ -29,16 +35,22 @@ pub(super) struct State {
29
35
30
36
impl PowerPolicy {
31
37
/// Computes the total requested power considering all current providers
32
- async fn compute_total_provider_power ( & self , new_request : bool ) -> Result < PowerState , Error > {
38
+ async fn compute_total_provider_power ( & self , new_request : bool ) -> PowerState {
33
39
let mut num_providers = if new_request { 1 } else { 0 } ;
34
40
35
41
for device in self . context . devices ( ) . await {
36
- let device = device. data :: < device:: Device > ( ) . ok_or ( Error :: InvalidDevice ) ?;
42
+ let device = device. data :: < device:: Device > ( ) ;
43
+ if device. is_none ( ) {
44
+ // A non-power device somehow got into the list of devices, note it and move on
45
+ warn ! ( "Found non-power device in devices list" ) ;
46
+ continue ;
47
+ }
37
48
49
+ let device = device. unwrap ( ) ;
38
50
if device. is_in_recovery ( ) . await {
39
51
// If any device is in recovery mode, we need to recover
40
52
info ! ( "Device {}: In recovery mode" , device. id( ) . 0 ) ;
41
- return Ok ( PowerState :: Recovery ) ;
53
+ return PowerState :: Recovery ;
42
54
}
43
55
44
56
if device. is_provider ( ) . await {
@@ -48,19 +60,25 @@ impl PowerPolicy {
48
60
49
61
let total_provided_power = num_providers * self . config . provider_unlimited . max_power_mw ( ) ;
50
62
if total_provided_power > self . config . limited_power_threshold_mw {
51
- Ok ( PowerState :: Limited )
63
+ PowerState :: Limited
52
64
} else {
53
- Ok ( PowerState :: Unlimited )
65
+ PowerState :: Unlimited
54
66
}
55
67
}
56
68
57
69
/// Update the power capability of all connected providers
58
70
/// Returns true if we need to enter recovery mode
59
- async fn update_provider_capability ( & self , target_power : PowerCapability ) -> Result < bool , Error > {
71
+ async fn update_provider_capability ( & self , target_power : PowerCapability , exit_on_recovery : bool ) -> bool {
60
72
let mut recovery = false ;
61
73
for device in self . context . devices ( ) . await {
62
- let device = device. data :: < device:: Device > ( ) . ok_or ( Error :: InvalidDevice ) ?;
74
+ let device = device. data :: < device:: Device > ( ) ;
75
+ if device. is_none ( ) {
76
+ // A non-power device somehow got into the list of devices, note it and move on
77
+ warn ! ( "Found non-power device in devices list" ) ;
78
+ continue ;
79
+ }
63
80
81
+ let device = device. unwrap ( ) ;
64
82
if let Ok ( action) = self
65
83
. context
66
84
. try_policy_action :: < action:: ConnectedProvider > ( device. id ( ) )
@@ -76,27 +94,34 @@ impl PowerPolicy {
76
94
) ;
77
95
78
96
if let Err ( _) = action. disconnect ( ) . await {
79
- error ! (
80
- "Device{}: Failed to disconnect provider, entering recovery mode" ,
81
- device. id( ) . 0
82
- ) ;
97
+ error ! ( "Device{}: Failed to disconnect provider" , device. id( ) . 0 ) ;
98
+
99
+ // Early exit if that's what we want
100
+ // This is used to avoid excessively switching power capabilities in the recovery flow
101
+ if exit_on_recovery {
102
+ return true ;
103
+ }
104
+
83
105
recovery = true ;
84
106
}
85
107
}
86
108
}
87
109
}
88
110
}
89
- Ok ( recovery)
111
+
112
+ return recovery;
90
113
}
91
114
92
115
/// Update the provider state of currently connected providers
93
- pub ( super ) async fn update_providers ( & self , new_provider : Option < DeviceId > ) -> Result < ( ) , Error > {
116
+ pub ( super ) async fn update_providers ( & self , new_provider : Option < DeviceId > ) {
94
117
trace ! ( "Updating providers" ) ;
95
118
let mut state = self . state . lock ( ) . await ;
119
+ let mut already_in_recovery = true ;
96
120
97
121
if state. current_provider_state . state != PowerState :: Recovery {
98
122
// Only update the power state if we're not in recovery mode
99
- state. current_provider_state . state = self . compute_total_provider_power ( new_provider. is_some ( ) ) . await ?;
123
+ already_in_recovery = false ;
124
+ state. current_provider_state . state = self . compute_total_provider_power ( new_provider. is_some ( ) ) . await ;
100
125
}
101
126
debug ! ( "New power state: {:?}" , state. current_provider_state. state) ;
102
127
@@ -106,24 +131,39 @@ impl PowerPolicy {
106
131
PowerState :: Limited => self . config . provider_limited ,
107
132
} ;
108
133
109
- let recovery = self . update_provider_capability ( target_power) . await ? ;
134
+ let recovery = self . update_provider_capability ( target_power, true ) . await ;
110
135
if let Some ( new_provider) = new_provider {
111
136
info ! ( "Connecting new provider" ) ;
112
- if let Ok ( action) = self . context . try_policy_action :: < action:: Idle > ( new_provider) . await {
113
- action. connect_provider ( target_power) . await ?;
137
+ let connected = if let Ok ( action) = self . context . try_policy_action :: < action:: Idle > ( new_provider) . await {
138
+ let target_power = if recovery {
139
+ // We entered recovery mode so attempt to connect at the recovery power
140
+ self . config . provider_recovery
141
+ } else {
142
+ target_power
143
+ } ;
144
+ action. connect_provider ( target_power) . await . is_ok ( )
114
145
} else {
115
- // Don't enter recovery mode if we can't connect to the new provider
116
- // Since it's a new provider that hasn't been connected then it's
117
- // not drawing power as far as we're concerned
146
+ false
147
+ } ;
148
+
149
+ // Don't enter recovery mode if we can't connect the new provider.
150
+ // Since it's a new provider that hasn't been connected then it's
151
+ // not drawing power as far as we're concerned
152
+ if !connected {
118
153
error ! ( "Device {}: Failed to connect provider" , new_provider. 0 ) ;
119
154
}
120
155
}
121
156
122
- if recovery {
157
+ if recovery && !already_in_recovery {
158
+ // Entering recovery, set power capability on all responding providers to recovery limit
159
+ // Don't check return value of update_provider_capability here, if we've spontaneously recovered
160
+ // then it'll get caught by the next call of attempt_provider_recovery
161
+ info ! ( "Entering recovery mode" ) ;
162
+ let _ = self
163
+ . update_provider_capability ( self . config . provider_recovery , false )
164
+ . await ;
123
165
state. current_provider_state . state = PowerState :: Recovery ;
124
166
}
125
-
126
- Ok ( ( ) )
127
167
}
128
168
129
169
pub ( super ) async fn attempt_provider_recovery ( & self ) {
@@ -134,10 +174,13 @@ impl PowerPolicy {
134
174
}
135
175
136
176
info ! ( "Attempting provider recovery" ) ;
177
+ let mut recovered = true ;
137
178
// Attempt to by disconnecting all providers in recovery
138
179
for device in self . context . devices ( ) . await {
139
180
let device = device. data :: < device:: Device > ( ) . ok_or ( Error :: InvalidDevice ) ;
140
181
if device. is_err ( ) {
182
+ // A non-power device somehow got into the list of devices, note it and move on
183
+ warn ! ( "Found non-power device in devices list" ) ;
141
184
continue ;
142
185
}
143
186
@@ -150,25 +193,25 @@ impl PowerPolicy {
150
193
{
151
194
if let Err ( _) = action. disconnect ( ) . await {
152
195
error ! ( "Device {}: Failed to recover" , device. id( ) . 0 ) ;
196
+ recovered = false ;
153
197
}
154
198
}
155
199
}
156
200
}
157
201
158
- // Attempt to restart in the limited power state
159
- self . state . lock ( ) . await . current_provider_state . state = PowerState :: Limited ;
160
- if self . update_providers ( None ) . await . is_err ( ) {
161
- // Failed to update providers, stay in recovery mode
162
- info ! ( "Failed to update providers, staying in recovery mode" ) ;
202
+ if !recovered {
203
+ info ! ( "Failed to recover all providers, staying in recovery mode" ) ;
163
204
return ;
164
205
}
165
206
166
- if self . state . lock ( ) . await . current_provider_state . state != PowerState :: Recovery {
167
- // Successfully recovered
168
- info ! ( "Successfully recovered from provider recovery mode" ) ;
169
- } else {
170
- // Still in recovery mode
171
- info ! ( "Still in provider recovery mode" ) ;
207
+ // Attempt to restart in the unlimited power state
208
+ self . state . lock ( ) . await . current_provider_state . state = PowerState :: Unlimited ;
209
+ self . update_providers ( None ) . await ;
210
+ if self . state . lock ( ) . await . current_provider_state . state == PowerState :: Recovery {
211
+ info ! ( "Failed to update providers, staying in recovery mode" ) ;
212
+ return ;
172
213
}
214
+
215
+ info ! ( "Successfully recovered from provider recovery mode" ) ;
173
216
}
174
217
}
0 commit comments