roo_wifi
API Documentation for roo_wifi
Loading...
Searching...
No Matches
controller.cpp
Go to the documentation of this file.
2
3namespace roo_wifi {
4
5namespace {
6
7ConnectionStatus getConnectionStatus(Interface::EventType type) {
8 switch (type) {
10 return WL_IDLE_STATUS;
12 return WL_CONNECTED;
14 return WL_DISCONNECTED;
16 return WL_CONNECT_FAILED;
18 return WL_CONNECTION_LOST;
19 default:
20 return WL_CONNECT_FAILED;
21 }
22}
23
24} // namespace
25
27 roo_scheduler::Scheduler& scheduler)
28 : store_(store),
29 interface_(interface),
30 enabled_(false),
31 current_network_(),
32 current_network_index_(-1),
33 current_network_status_(WL_NO_SSID_AVAIL),
34 all_networks_(),
35 wifi_listener_(*this),
36 model_listeners_(),
37 connecting_(false),
38 start_scan_(scheduler, [this]() { startScan(); }),
39 refresh_current_network_(scheduler,
40 [this]() { periodicRefreshCurrentNetwork(); }) {}
41
42Controller::~Controller() { interface_.removeEventListener(&wifi_listener_); }
43
45 interface_.addEventListener(&wifi_listener_);
46 enabled_ = store_.getIsInterfaceEnabled();
47 if (enabled_) notifyEnableChanged();
48 std::string ssid = store_.getDefaultSSID();
49 if (enabled_ && !ssid.empty()) {
50 connect();
51 }
52}
53
55 model_listeners_.insert(listener);
56}
57
59 model_listeners_.erase(listener);
60}
61
63 int count = all_networks_.size();
64 if (current_network_index_ >= 0) --count;
65 return count;
66}
67
69 return current_network_;
70}
71
73 const std::string& ssid) const {
74 for (const Network& net : all_networks_) {
75 if (net.ssid == ssid) return &net;
76 }
77 return nullptr;
78}
79
81 return current_network_status_;
82}
83
85 if (current_network_index_ >= 0 && idx >= current_network_index_) {
86 idx++;
87 }
88 return all_networks_[idx];
89}
90
92 bool started = interface_.startScan();
93 if (started) {
94 for (auto& l : model_listeners_) {
95 l->onScanStarted();
96 };
97 }
98 return started;
99}
100
102 enabled_ = !enabled_;
103 store_.setIsInterfaceEnabled(enabled_);
104 if (!enabled_) {
105 interface_.disconnect();
106 }
107 connecting_ = false;
109 if (enabled_) {
110 resume();
111 } else {
112 pause();
113 }
114}
115
117 for (auto& l : model_listeners_) {
118 l->onEnableChanged(enabled_);
119 };
120}
121
122bool Controller::getStoredPassword(const std::string& ssid,
123 std::string& passwd) const {
124 return store_.getPassword(ssid, passwd);
125}
126
127void Controller::pause() { start_scan_.cancel(); }
128
130 if (!enabled_) return;
131 refreshCurrentNetwork();
132 if (!refresh_current_network_.is_scheduled()) {
133 refresh_current_network_.scheduleAfter(roo_time::Seconds(2));
134 }
135 if (interface_.scanCompleted()) {
136 for (auto& l : model_listeners_) {
137 l->onScanCompleted();
138 };
139 start_scan_.scheduleAfter(roo_time::Seconds(15));
140 } else {
141 startScan();
142 }
143}
144
145void Controller::setPassword(const std::string& ssid,
146 const std::string& passwd) {
147 store_.setPassword(ssid, passwd);
148}
149
151 std::string ssid = store_.getDefaultSSID();
152 std::string password;
153 store_.getPassword(ssid, password);
154 return connect(ssid, password);
155}
156
157bool Controller::connect(const std::string& ssid, const std::string& passwd) {
158 std::string default_ssid = store_.getDefaultSSID();
159 if (ssid != default_ssid) {
160 store_.setDefaultSSID(ssid);
161 }
162 std::string current_password;
163 if (!passwd.empty() && (!store_.getPassword(ssid, current_password) ||
164 current_password != passwd)) {
165 store_.setPassword(ssid, passwd);
166 }
167 if (!interface_.connect(ssid, passwd)) return false;
168 connecting_ = true;
169 const Network* in_range = lookupNetwork(ssid);
170 if (in_range == nullptr) {
171 updateCurrentNetwork(ssid, passwd.empty(), -128, WL_DISCONNECTED, true);
172 } else {
173 updateCurrentNetwork(ssid, in_range->open, in_range->rssi, WL_DISCONNECTED,
174 true);
175 }
176 return true;
177}
178
180 connecting_ = false;
181 interface_.disconnect();
182}
183
184void Controller::forget(const std::string& ssid) {
185 store_.clearPassword(ssid);
186 if (ssid == store_.getDefaultSSID()) {
187 store_.clearDefaultSSID();
188 }
189}
190
191void Controller::onConnectionStateChanged(Interface::EventType type) {
192 if (type == Interface::EV_UNKNOWN) return;
193 if (type == Interface::EV_DISCONNECTED ||
196 connecting_ = false;
197 }
198 updateCurrentNetwork(current_network_.ssid, current_network_.open,
199 current_network_.rssi, getConnectionStatus(type), true);
200 for (auto& l : model_listeners_) {
201 l->onConnectionStateChanged(type);
202 }
203}
204
205void Controller::periodicRefreshCurrentNetwork() {
206 refreshCurrentNetwork();
207 if (isEnabled()) {
208 refresh_current_network_.scheduleAfter(roo_time::Seconds(2));
209 }
210}
211
212void Controller::refreshCurrentNetwork() {
213 // If we're connected to the network, this is it.
214 NetworkDetails current;
215 if (interface_.getApInfo(&current)) {
216 updateCurrentNetwork(std::string((const char*)current.ssid,
217 strlen((const char*)current.ssid)),
218 (current.authmode == WIFI_AUTH_OPEN), current.rssi,
219 current.status, false);
220 } else {
221 // Check if we have a default network.
222 std::string default_ssid = store_.getDefaultSSID();
223 const Network* default_network_in_range = nullptr;
224 if (!default_ssid.empty()) {
225 // See if the default network is in range according to the latest
226 // scan results.
227 default_network_in_range = lookupNetwork(default_ssid);
228 }
229 // Keep erroneous states sticky. Only update if the network has actually
230 // changed.
231 if (default_network_in_range == nullptr) {
232 ConnectionStatus new_status = (default_ssid == current_network_.ssid)
233 ? current_network_status_
235 updateCurrentNetwork(default_ssid, true, -128, new_status, false);
236 } else {
237 ConnectionStatus new_status = (default_ssid == current_network_.ssid)
238 ? current_network_status_
240 updateCurrentNetwork(default_ssid, default_network_in_range->open,
241 default_network_in_range->rssi, new_status, false);
242 }
243 }
244}
245
246void Controller::updateCurrentNetwork(const std::string& ssid, bool open,
247 int8_t rssi, ConnectionStatus status,
248 bool force_notify) {
249 if (!force_notify && rssi == current_network_.rssi &&
250 ssid == current_network_.ssid && open == current_network_.open &&
251 status == current_network_status_) {
252 return;
253 }
254 current_network_.ssid = ssid;
255 current_network_.open = open;
256 current_network_.rssi = rssi;
257 current_network_status_ = status;
258 current_network_index_ = -1;
259 for (size_t i = 0; i < all_networks_.size(); ++i) {
260 if (all_networks_[i].ssid == ssid) {
261 current_network_index_ = static_cast<int16_t>(i);
262 break;
263 }
264 }
265 for (auto& l : model_listeners_) {
266 l->onCurrentNetworkChanged();
267 };
268}
269
270void Controller::onScanCompleted() {
271 current_network_index_ = -1;
272 std::vector<NetworkDetails> raw_data;
273 interface_.getScanResults(&raw_data, 100);
274 int raw_count = raw_data.size();
275 if (raw_count == 0) {
276 all_networks_.clear();
277 return;
278 }
279 // De-duplicate SSID, keeping the one with the strongest signal.
280 // Start by sorting by (ssid, signal strength).
281 std::vector<uint8_t> indices(raw_data.size(), 0);
282 for (uint8_t i = 0; i < raw_count; ++i) indices[i] = i;
283 std::sort(&indices[0], &indices[raw_count], [&](int a, int b) -> bool {
284 int ssid_cmp = strncmp((const char*)raw_data[a].ssid,
285 (const char*)raw_data[b].ssid, 33);
286 if (ssid_cmp < 0) return true;
287 if (ssid_cmp > 0) return false;
288 return raw_data[a].rssi > raw_data[b].rssi;
289 });
290 // Now, compact the result by keeping the first value for each SSID.
291 const char* current_ssid = (const char*)raw_data[indices[0]].ssid;
292 uint8_t src = 1;
293 uint8_t dst = 1;
294 while (src < raw_count) {
295 const char* candidate_ssid = (const char*)raw_data[indices[src]].ssid;
296 if (strncmp(current_ssid, candidate_ssid, 33) != 0) {
297 current_ssid = candidate_ssid;
298 indices[dst++] = indices[src];
299 }
300 ++src;
301 }
302 // Now sort again, this time by signal strength only.
303 // Single-out and remove the default network.
304 std::sort(&indices[0], &indices[dst], [&](int a, int b) -> bool {
305 return raw_data[a].rssi > raw_data[b].rssi;
306 });
307 // Finally, copy over the results.
308 all_networks_.resize(dst);
309 bool found = false;
310 for (uint8_t i = 0; i < dst; ++i) {
311 NetworkDetails& src = raw_data[indices[i]];
312 Network& dst = all_networks_[i];
313 dst.ssid =
314 std::string((const char*)src.ssid, strlen((const char*)src.ssid));
315 dst.open = (src.authmode == WIFI_AUTH_OPEN);
316 dst.rssi = src.rssi;
317 if (dst.ssid == current_network_.ssid) {
318 found = true;
319 current_network_index_ = i;
320 if (current_network_status_ == WL_NO_SSID_AVAIL) {
321 current_network_status_ = WL_DISCONNECTED;
322 }
323 }
324 }
325 if (!found && current_network_status_ == WL_DISCONNECTED) {
326 current_network_status_ = WL_NO_SSID_AVAIL;
327 }
328 for (auto& l : model_listeners_) {
329 l->onScanCompleted();
330 };
331 if (enabled_) {
332 start_scan_.scheduleAfter(roo_time::Seconds(15));
333 }
334}
335
336} // namespace roo_wifi
Listener for controller events.
Definition controller.h:29
const Network & currentNetwork() const
Returns the current network (may be empty if disconnected).
void pause()
Temporarily disables periodic refresh and event processing.
ConnectionStatus currentNetworkStatus() const
Returns the connection status of the current network.
void toggleEnabled()
Toggles the enabled/disabled state and persists it in the store.
void begin()
Initializes the controller and registers for interface events.
void resume()
Resumes periodic refresh and event processing.
bool isEnabled() const
Returns true when the interface is enabled.
Definition controller.h:81
bool connect()
Connects using stored SSID/password values.
void removeListener(Listener *listener)
Removes a previously added listener.
void forget(const std::string &ssid)
Forgets the password and SSID association.
bool startScan()
Starts a scan. Returns false if a scan could not be started.
void disconnect()
Disconnects the current connection.
~Controller()
Destroys the controller and detaches listeners.
bool getStoredPassword(const std::string &ssid, std::string &passwd) const
Looks up a stored password for the given SSID.
void setPassword(const std::string &ssid, const std::string &passwd)
Stores a password for the given SSID.
int otherScannedNetworksCount() const
Returns the number of non-current networks in the scan list.
Controller(Store &store, Interface &interface, roo_scheduler::Scheduler &scheduler)
Creates a controller using the provided store, interface, and scheduler.
void addListener(Listener *listener)
Adds a listener for controller events.
const Network * lookupNetwork(const std::string &ssid) const
Returns a network by SSID, or nullptr if not found.
void notifyEnableChanged()
Notifies listeners that enable state changed.
const Network & otherNetwork(int idx) const
Returns the ith non-current network in the scan list.
Abstraction for interacting with the hardware Wi-Fi interface.
Definition interface.h:70
virtual void removeEventListener(EventListener *listener)=0
Unregisters an interface event listener.
virtual void disconnect()=0
Disconnects from the current network.
EventType
Interface event types.
Definition interface.h:73
virtual bool getApInfo(NetworkDetails *info) const =0
Returns current AP information; false if not connected.
virtual bool startScan()=0
Starts a scan.
virtual void addEventListener(EventListener *listener)=0
Registers an interface event listener.
virtual bool getScanResults(std::vector< NetworkDetails > *list, int max_count) const =0
Returns scan results, up to max_count entries.
virtual bool scanCompleted() const =0
Returns true if the last scan has completed.
virtual bool connect(const std::string &ssid, const std::string &passwd)=0
Connects to the specified SSID/password.
Abstraction for persistently storing Wi-Fi controller data.
Definition store.h:14
virtual void clearDefaultSSID()=0
Clears the default SSID.
virtual bool getPassword(const std::string &ssid, std::string &password)=0
Retrieves a stored password for an SSID.
virtual void setPassword(const std::string &ssid, roo::string_view password)=0
Stores a password for an SSID.
virtual bool getIsInterfaceEnabled()=0
Returns whether the Wi-Fi interface is enabled.
virtual std::string getDefaultSSID()=0
Returns the default SSID, if any.
virtual void clearPassword(const std::string &ssid)=0
Clears a stored password for an SSID.
virtual void setDefaultSSID(const std::string &ssid)=0
Sets the default SSID.
virtual void setIsInterfaceEnabled(bool enabled)=0
Sets whether the Wi-Fi interface is enabled.
ConnectionStatus
Wi-Fi connection status.
Definition interface.h:42
@ WL_IDLE_STATUS
Definition interface.h:43
@ WL_DISCONNECTED
Definition interface.h:49
@ WL_CONNECTION_LOST
Definition interface.h:48
@ WL_CONNECT_FAILED
Definition interface.h:47
@ WL_CONNECTED
Definition interface.h:46
@ WL_NO_SSID_AVAIL
Definition interface.h:44
@ WIFI_AUTH_OPEN
Open.
Definition interface.h:12
Summary of a scanned network.
Definition controller.h:20