diff --git a/.github/workflows/build_release_bins.yml b/.github/workflows/build_release_bins.yml index 4e58bb5..01cc76f 100644 --- a/.github/workflows/build_release_bins.yml +++ b/.github/workflows/build_release_bins.yml @@ -7,6 +7,7 @@ on: branches: - "master" - "main" + - "feature/improv" pull_request: branches: - "master" diff --git a/ESP/ini/dev_config.ini b/ESP/ini/dev_config.ini index eee1874..88300da 100644 --- a/ESP/ini/dev_config.ini +++ b/ESP/ini/dev_config.ini @@ -19,6 +19,7 @@ lib_ldf_mode = deep+ ;115200 is used for compatability - if you are on windows and want the code to flash faster use 921600 upload_speed = 921600 lib_deps = + esp32-camera https://github.com/me-no-dev/ESPAsyncWebServer.git https://github.com/me-no-dev/AsyncTCP.git extra_scripts = @@ -29,7 +30,8 @@ build_flags = -DADHOC_CHANNEL=${wifi.adhocchannel} -DWIFI_CHANNEL=${wifi.channel} -DDEBUG_ESP_PORT=Serial - '-DMDNS_HOSTNAME=${wifi.mdnsname}' + '-DVERSION=""' ; set the debug port + '-DMDNS_HOSTNAME=${wifi.mdnsname}' ; Set the OTA password '-DWIFI_SSID=${wifi.ssid}' '-DWIFI_PASSWORD=${wifi.password}' '-DWIFI_AP_SSID=${wifi.ap_ssid}' diff --git a/ESP/ini/sim.ini b/ESP/ini/sim.ini index f8d8fd4..9c4fe87 100644 --- a/ESP/ini/sim.ini +++ b/ESP/ini/sim.ini @@ -19,6 +19,7 @@ build_flags = -DENABLE_ADHOC=${wifi.enableadhoc} '-DWIFI_AP_SSID=${wifi.ap_ssid}' '-DWIFI_AP_PASSWORD=${wifi.ap_password}' '-DWIFI_AP_CHANNEL=${wifi.adhocchannel}' + '-DVERSION=""' -DOTA_SERVER_PORT=${ota.otaserverport} '-DOTA_PASSWORD=${ota.otapassword}' ; Set the OTA password diff --git a/ESP/lib/src/data/StateManager/StateManager.cpp b/ESP/lib/src/data/StateManager/StateManager.cpp index 427faad..3e903e7 100644 --- a/ESP/lib/src/data/StateManager/StateManager.cpp +++ b/ESP/lib/src/data/StateManager/StateManager.cpp @@ -7,3 +7,4 @@ StateManager mdnsStateManager; StateManager cameraStateManager; StateManager ledStateManager; StateManager streamStateManager; +StateManager configStateManager; diff --git a/ESP/lib/src/data/StateManager/StateManager.hpp b/ESP/lib/src/data/StateManager/StateManager.hpp index 5522b12..f2b207d 100644 --- a/ESP/lib/src/data/StateManager/StateManager.hpp +++ b/ESP/lib/src/data/StateManager/StateManager.hpp @@ -6,74 +6,68 @@ * StateManager * All Project States are managed here */ -struct DeviceStates -{ - enum State_e - { - Starting, - Started, - Stopping, - Stopped, - Error - }; +struct DeviceStates { + enum LEDStates_e { + _LedStateNone, + _Improv_Start, + _Improv_Stop, + _Improv_Processing, + _Improv_Error, + _WebServerState_Error, + _WiFiState_Error, + _MDNSState_Error, + _Camera_Error, + _WiFiState_Connecting, + _WiFiState_Connected + }; - enum LEDStates_e - { - _LedStateNone, - _SerialManager_Error, - _WebServerState_Error, - _WiFiState_Error, - _MDNSState_Error, - _Camera_Error, - _WiFiState_Connecting, - _WiFiState_Connected - }; + enum ConfigState_e { + configLoaded, + deviceConfigUpdated, + mdnsConfigUpdated, + networksConfigUpdated, + apConfigUpdated, + wifiTxPowerUpdated, + cameraConfigUpdated + }; - enum WiFiState_e - { - WiFiState_None, - WiFiState_Idle, - WiFiState_Disconnected, - WiFiState_Connecting, - WiFiState_Connected, - WiFiState_ADHOC, - WiFiState_Error - }; + enum WiFiState_e { + WiFiState_None, + WiFiState_Idle, + WiFiState_Disconnected, + WiFiState_Connecting, + WiFiState_Connected, + WiFiState_ADHOC, + WiFiState_Error + }; - enum WebServerState_e - { - WebServerState_Stopped, - WebServerState_Starting, - WebServerState_Started, - WebServerState_Stopping, - WebServerState_Error - }; + enum WebServerState_e { + WebServerState_Stopped, + WebServerState_Starting, + WebServerState_Started, + WebServerState_Stopping, + WebServerState_Error + }; - enum MDNSState_e - { - MDNSState_Stopped, - MDNSState_Starting, - MDNSState_Started, - MDNSState_Stopping, - MDNSState_Error, - MDNSState_QueryStarted, - MDNSState_QueryComplete - }; + enum MDNSState_e { + MDNSState_Stopped, + MDNSState_Starting, + MDNSState_Started, + MDNSState_Stopping, + MDNSState_Error, + MDNSState_QueryStarted, + MDNSState_QueryComplete + }; - enum CameraState_e - { - Camera_Disconnected, - Camera_Success, - Camera_Connected, - Camera_Error - }; + enum CameraState_e { + Camera_Disconnected, + Camera_Success, + Camera_Connected, + Camera_Error + }; - enum StreamState_e - { - Stream_OFF, - Stream_ON, - Stream_Error - }; + enum StreamState_e { Stream_OFF, Stream_ON, Stream_Error }; + enum State_e { Starting, Started, Stopping, Stopped, Error }; }; /* @@ -81,34 +75,25 @@ struct DeviceStates * All Project Events are managed here */ template -class StateManager -{ -public: - StateManager() { - this->_current_state = static_cast(0); - } +class StateManager { + public: + StateManager() { this->_current_state = static_cast(0); } - virtual ~StateManager() {} + virtual ~StateManager() {} - /* - * @brief Sets the state of the stateManager - * @param T state - the state to be set - */ - void setState(T state) - { - _current_state = state; - } + /* + * @brief Sets the state of the stateManager + * @param T state - the state to be set + */ + void setState(T state) { _current_state = state; } - /* - * @brief Returns the current state of the stateManager - */ - T getCurrentState() - { - return _current_state; - } + /* + * @brief Returns the current state of the stateManager + */ + T getCurrentState() { return _current_state; } -private: - T _current_state; + private: + T _current_state; }; typedef DeviceStates::State_e State_e; @@ -118,6 +103,7 @@ typedef DeviceStates::MDNSState_e MDNSState_e; typedef DeviceStates::CameraState_e CameraState_e; typedef DeviceStates::LEDStates_e LEDStates_e; typedef DeviceStates::StreamState_e StreamState_e; +typedef DeviceStates::ConfigState_e ConfigState_e; extern StateManager stateManager; extern StateManager wifiStateManager; @@ -126,5 +112,6 @@ extern StateManager mdnsStateManager; extern StateManager cameraStateManager; extern StateManager ledStateManager; extern StateManager streamStateManager; +extern StateManager configStateManager; -#endif // STATEMANAGER_HPP +#endif // STATEMANAGER_HPP diff --git a/ESP/lib/src/data/config/project_config.cpp b/ESP/lib/src/data/config/project_config.cpp index fc19272..0ad5bd5 100644 --- a/ESP/lib/src/data/config/project_config.cpp +++ b/ESP/lib/src/data/config/project_config.cpp @@ -21,8 +21,8 @@ void ProjectConfig::initConfig() { bool success = begin(_name.c_str()); - log_i("Config name: %s", _name.c_str()); - log_i("Config loaded: %s", success ? "true" : "false"); + log_i("[Project Config]: Config name: %s", _name.c_str()); + log_i("[Project Config]: Config loaded: %s", success ? "true" : "false"); /* * If the config is not loaded, @@ -41,7 +41,7 @@ void ProjectConfig::initConfig() { "openiristracker", }; - log_i("MDNS name: %s", _mdnsName.c_str()); + log_i("[Project Config]: MDNS name: %s", _mdnsName.c_str()); this->config.ap_network = { "", @@ -104,7 +104,7 @@ void ProjectConfig::wifiConfigSave() { putString("apPass", this->config.ap_network.password.c_str()); putUInt("apChannel", this->config.ap_network.channel); - log_i("Project config saved and system is rebooting"); + log_i("[Project Config]: Wifi configs saved"); } void ProjectConfig::deviceConfigSave() { @@ -201,7 +201,7 @@ void ProjectConfig::load() { this->config.camera.brightness = getInt("brightness", 2); this->_already_loaded = true; - this->notify(ObserverEvent::configLoaded); + this->notifyAll(ConfigState_e::configLoaded); } //********************************************************************************************************************** @@ -219,7 +219,7 @@ void ProjectConfig::setDeviceConfig(const std::string& OTALogin, this->config.device.OTAPort = OTAPort; if (shouldNotify) - this->notify(ObserverEvent::deviceConfigUpdated); + this->notifyAll(ConfigState_e::deviceConfigUpdated); } void ProjectConfig::setMDNSConfig(const std::string& hostname, @@ -230,64 +230,58 @@ void ProjectConfig::setMDNSConfig(const std::string& hostname, this->config.mdns.service.assign(service); if (shouldNotify) - this->notify(ObserverEvent::mdnsConfigUpdated); + this->notifyAll(ConfigState_e::mdnsConfigUpdated); } -void ProjectConfig::setCameraConfig(uint8_t* vflip, - uint8_t* framesize, - uint8_t* href, - uint8_t* quality, - uint8_t* brightness, +void ProjectConfig::setCameraConfig(uint8_t vflip, + uint8_t framesize, + uint8_t href, + uint8_t quality, + uint8_t brightness, bool shouldNotify) { log_d("Updating camera config"); - this->config.camera.vflip = *vflip; - this->config.camera.href = *href; - this->config.camera.framesize = *framesize; - this->config.camera.quality = *quality; - this->config.camera.brightness = *brightness; + this->config.camera.vflip = vflip; + this->config.camera.href = href; + this->config.camera.framesize = framesize; + this->config.camera.quality = quality; + this->config.camera.brightness = brightness; log_d("Updating Camera config"); if (shouldNotify) - this->notify(ObserverEvent::cameraConfigUpdated); + this->notifyAll(ConfigState_e::cameraConfigUpdated); } void ProjectConfig::setWifiConfig(const std::string& networkName, const std::string& ssid, const std::string& password, - uint8_t* channel, - uint8_t* power, + uint8_t channel, + uint8_t power, bool adhoc, bool shouldNotify) { // we store the ADHOC flag as false because the networks we store in the // config are the ones we want the esp to connect to, rather than host as AP, // and here we're just updating them size_t size = this->config.networks.size(); - // we're allowing to store up to three additional networks - if (size == 0) { - Serial.println("No networks, We're adding a new network"); - this->config.networks.emplace_back(networkName, ssid, password, *channel, - *power, false); - - if (shouldNotify) - this->notify(ObserverEvent::networksConfigUpdated); - - return; - } for (auto it = this->config.networks.begin(); it != this->config.networks.end();) { if (it->name == networkName) { - log_i("Found network %s, updating it ...", it->name.c_str()); + log_i("[Project Config]: Found network %s, updating it ...", + it->name.c_str()); it->name = networkName; it->ssid = ssid; it->password = password; - it->channel = *channel; - it->power = *power; + it->channel = channel; + it->power = power; it->adhoc = false; - if (shouldNotify) - this->notify(ObserverEvent::networksConfigUpdated); + if (shouldNotify) { + wifiStateManager.setState(WiFiState_e::WiFiState_Disconnected); + WiFi.disconnect(); + this->wifiConfigSave(); + this->notifyAll(ConfigState_e::networksConfigUpdated); + } return; } else { @@ -295,17 +289,28 @@ void ProjectConfig::setWifiConfig(const std::string& networkName, } } - if (size < 3) { + if (size < 3 && size > 0) { Serial.println("We're adding a new network"); // we don't have that network yet, we can add it as we still have some // space we're using emplace_back as push_back will create a copy of it, // we want to avoid that - this->config.networks.emplace_back(networkName, ssid, password, *channel, - *power, false); + this->config.networks.emplace_back(networkName, ssid, password, channel, + power, false); } - if (shouldNotify) - this->notify(ObserverEvent::networksConfigUpdated); + // we're allowing to store up to three additional networks + if (size == 0) { + Serial.println("No networks, We're adding a new network"); + this->config.networks.emplace_back(networkName, ssid, password, channel, + power, false); + } + + if (shouldNotify) { + wifiStateManager.setState(WiFiState_e::WiFiState_None); + WiFi.disconnect(); + this->wifiConfigSave(); + this->notifyAll(ConfigState_e::networksConfigUpdated); + } } void ProjectConfig::deleteWifiConfig(const std::string& networkName, @@ -318,40 +323,47 @@ void ProjectConfig::deleteWifiConfig(const std::string& networkName, for (auto it = this->config.networks.begin(); it != this->config.networks.end();) { if (it->name == networkName) { - log_i("Found network %s", it->name.c_str()); + log_i("[Project Config]: Found network %s", it->name.c_str()); it = this->config.networks.erase(it); - log_i("Deleted network %s", networkName.c_str()); + log_i("[Project Config]: Deleted network %s", networkName.c_str()); } else { ++it; } } - if (shouldNotify) - this->notify(ObserverEvent::networksConfigUpdated); + if (shouldNotify) { + this->wifiConfigSave(); + this->notifyAll(ConfigState_e::networksConfigUpdated); + } } -void ProjectConfig::setWiFiTxPower(uint8_t* power, bool shouldNotify) { - this->config.txpower.power = *power; +void ProjectConfig::setWiFiTxPower(uint8_t power, bool shouldNotify) { + this->config.txpower.power = power; log_d("Updating wifi tx power"); if (shouldNotify) - this->notify(ObserverEvent::wifiTxPowerUpdated); + this->notifyAll(ConfigState_e::wifiTxPowerUpdated); } void ProjectConfig::setAPWifiConfig(const std::string& ssid, const std::string& password, - uint8_t* channel, + uint8_t channel, bool adhoc, bool shouldNotify) { this->config.ap_network.ssid.assign(ssid); this->config.ap_network.password.assign(password); - this->config.ap_network.channel = *channel; + this->config.ap_network.channel = channel; this->config.ap_network.adhoc = adhoc; log_d("Updating access point config"); - if (shouldNotify) - this->notify(ObserverEvent::networksConfigUpdated); + + if (shouldNotify) { + wifiStateManager.setState(WiFiState_e::WiFiState_None); + WiFi.disconnect(); + this->wifiConfigSave(); + this->notifyAll(ConfigState_e::networksConfigUpdated); + } } std::string ProjectConfig::DeviceConfig_t::toRepresentation() { @@ -409,21 +421,21 @@ std::string ProjectConfig::WiFiTxPower_t::toRepresentation() { //* //********************************************************************************************************************** -ProjectConfig::DeviceConfig_t* ProjectConfig::getDeviceConfig() { - return &this->config.device; +ProjectConfig::DeviceConfig_t& ProjectConfig::getDeviceConfig() { + return this->config.device; } -ProjectConfig::CameraConfig_t* ProjectConfig::getCameraConfig() { - return &this->config.camera; +ProjectConfig::CameraConfig_t& ProjectConfig::getCameraConfig() { + return this->config.camera; } -std::vector* ProjectConfig::getWifiConfigs() { - return &this->config.networks; +std::vector& ProjectConfig::getWifiConfigs() { + return this->config.networks; } -ProjectConfig::AP_WiFiConfig_t* ProjectConfig::getAPWifiConfig() { - return &this->config.ap_network; +ProjectConfig::AP_WiFiConfig_t& ProjectConfig::getAPWifiConfig() { + return this->config.ap_network; } -ProjectConfig::MDNSConfig_t* ProjectConfig::getMDNSConfig() { - return &this->config.mdns; +ProjectConfig::MDNSConfig_t& ProjectConfig::getMDNSConfig() { + return this->config.mdns; } -ProjectConfig::WiFiTxPower_t* ProjectConfig::getWiFiTxPowerConfig() { - return &this->config.txpower; +ProjectConfig::WiFiTxPower_t& ProjectConfig::getWiFiTxPowerConfig() { + return this->config.txpower; } diff --git a/ESP/lib/src/data/config/project_config.hpp b/ESP/lib/src/data/config/project_config.hpp index 9775a2b..a2a5c99 100644 --- a/ESP/lib/src/data/config/project_config.hpp +++ b/ESP/lib/src/data/config/project_config.hpp @@ -7,11 +7,13 @@ #include #include +#include "data/StateManager/StateManager.hpp" #include "data/utilities/Observer.hpp" #include "data/utilities/helpers.hpp" +#include "data/utilities/network_utilities.hpp" #include "tasks/tasks.hpp" -class ProjectConfig : public Preferences, public ISubject { +class ProjectConfig : public Preferences, public ISubject { public: ProjectConfig(const std::string& name = std::string(), const std::string& mdnsName = std::string()); @@ -95,12 +97,12 @@ class ProjectConfig : public Preferences, public ISubject { WiFiTxPower_t txpower; }; - DeviceConfig_t* getDeviceConfig(); - CameraConfig_t* getCameraConfig(); - std::vector* getWifiConfigs(); - AP_WiFiConfig_t* getAPWifiConfig(); - MDNSConfig_t* getMDNSConfig(); - WiFiTxPower_t* getWiFiTxPowerConfig(); + DeviceConfig_t& getDeviceConfig(); + CameraConfig_t& getCameraConfig(); + std::vector& getWifiConfigs(); + AP_WiFiConfig_t& getAPWifiConfig(); + MDNSConfig_t& getMDNSConfig(); + WiFiTxPower_t& getWiFiTxPowerConfig(); void setDeviceConfig(const std::string& OTALogin, const std::string& OTAPassword, @@ -109,25 +111,25 @@ class ProjectConfig : public Preferences, public ISubject { void setMDNSConfig(const std::string& hostname, const std::string& service, bool shouldNotify); - void setCameraConfig(uint8_t* vflip, - uint8_t* framesize, - uint8_t* href, - uint8_t* quality, - uint8_t* brightness, + void setCameraConfig(uint8_t vflip, + uint8_t framesize, + uint8_t href, + uint8_t quality, + uint8_t brightness, bool shouldNotify); void setWifiConfig(const std::string& networkName, const std::string& ssid, const std::string& password, - uint8_t* channel, - uint8_t* power, + uint8_t channel, + uint8_t power, bool adhoc, bool shouldNotify); void setAPWifiConfig(const std::string& ssid, const std::string& password, - uint8_t* channel, + uint8_t channel, bool adhoc, bool shouldNotify); - void setWiFiTxPower(uint8_t* power, bool shouldNotify); + void setWiFiTxPower(uint8_t power, bool shouldNotify); void deleteWifiConfig(const std::string& networkName, bool shouldNotify); diff --git a/ESP/lib/src/data/utilities/Observer.hpp b/ESP/lib/src/data/utilities/Observer.hpp index f9265dd..05ac1b6 100644 --- a/ESP/lib/src/data/utilities/Observer.hpp +++ b/ESP/lib/src/data/utilities/Observer.hpp @@ -1,50 +1,60 @@ -#pragma once #ifndef OBSERVER_HPP #define OBSERVER_HPP -#include +#include +#include +#include -namespace ObserverEvent -{ - enum Event - { - configLoaded = 1, - deviceConfigUpdated = 2, - cameraConfigUpdated = 3, - networksConfigUpdated = 4, - mdnsConfigUpdated = 5, - wifiTxPowerUpdated = 6, - }; -} - -class IObserver -{ -public: - virtual void update(ObserverEvent::Event event) = 0; +template +class IObserver { + public: + virtual void update(EnumT event) = 0; + virtual std::string getName() = 0; }; -class ISubject -{ -private: - std::set observers; +template +class ISubject { + private: + typedef IObserver& Observer_t; + typedef std::unordered_map Observers_t; -public: - void attach(IObserver *observer) - { - this->observers.insert(observer); + Observers_t observers; + + public: + void attach(Observer_t observer) { + this->observers.emplace(observer.getName(), observer); } - void detach(IObserver *observer) - { - this->observers.erase(observer); + void detach(Observer_t observer) { + // Note: delete pointer + delete (*this->observers.find(observer.getName())).second; + this->observers.erase(observer.getName()); } - void notify(ObserverEvent::Event event) - { - for (auto observer : this->observers) - { - observer->update(event); + void detachAll() { + // Note: delete pointers + for (auto observer = observers.begin(); observer != observers.end(); + ++observer) { + delete (*observer).second; + } + // Note: clear map + this->observers.clear(); + } + + void notifyAll(EnumT event) { + for (auto observer = observers.begin(); observer != observers.end(); + ++observer) { + (*observer).second.update(event); } } -}; -#endif // !OBSERVER_HPP \ No newline at end of file + void notify(EnumT event, const std::string& observerName) { + auto it_map = observers.find(observerName); + if (it_map != observers.end()) { + (*it_map).second->update(event); + return; + } + log_e("Invalid Map Index"); + return; + } +}; +#endif // OBSERVER_HPP diff --git a/ESP/lib/src/data/utilities/network_utilities.cpp b/ESP/lib/src/data/utilities/network_utilities.cpp index 3c4a6d0..f89f861 100644 --- a/ESP/lib/src/data/utilities/network_utilities.cpp +++ b/ESP/lib/src/data/utilities/network_utilities.cpp @@ -5,7 +5,7 @@ void Network_Utilities::setupWifiScan() // Set WiFi to station mode and disconnect from an AP if it was previously connected WiFi.mode(WIFI_STA); WiFi.disconnect(); // Disconnect from the access point if connected before - my_delay(0.1L); + my_delay(0.1); Serial.println("Setup done"); } diff --git a/ESP/lib/src/data/utilities/string_view.hpp b/ESP/lib/src/data/utilities/string_view.hpp new file mode 100644 index 0000000..9a4ba4f --- /dev/null +++ b/ESP/lib/src/data/utilities/string_view.hpp @@ -0,0 +1,1245 @@ +#ifndef STRING_VIEW_HPP +#define STRING_VIEW_HPP + +#include // std:: +#include // std::size_t +#include // std::reverse_iterator +#include // std::allocator +#include // std::basic_ostream +#include // std::out_of_range +#include // std::char_traits +#include "helpers.hpp" +namespace Helpers { + + //////////////////////////////////////////////////////////////////////////// + /// \brief A wrapper around non-owned strings. + /// + /// This is an implementation of the C++17 string_view proposal + /// + /// \ingroup core + //////////////////////////////////////////////////////////////////////////// + template > + class basic_string_view final { + //------------------------------------------------------------------------ + // Public Member Types + //------------------------------------------------------------------------ + public: + using char_type = CharT; + using traits_type = Traits; + using size_type = std::size_t; + + using value_type = CharT; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using iterator = const CharT*; + using const_iterator = const CharT*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + //------------------------------------------------------------------------ + // Public Members + //------------------------------------------------------------------------ + public: + static constexpr size_type npos = size_type(-1); + + //------------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------------ + public: + /// \brief Default constructs a basic_string_view without any content + constexpr basic_string_view() noexcept; + + /// \brief Constructs a basic_string_view by copying another one + /// + /// \param other the string view being copied + constexpr basic_string_view(const basic_string_view& other) noexcept = + default; + + /// \brief Constructs a basic_string_view by moving anothe rone + /// + /// \param other the string view being moved + constexpr basic_string_view(basic_string_view&& other) noexcept = default; + + /// \brief Constructs a basic_string_view from a std::basic_string + /// + /// \param str the string to view + template + basic_string_view( + const std::basic_string& str) noexcept; + + /// \brief Constructs a basic_string_view from an ansi-string + /// + /// \param str the string to view + constexpr basic_string_view(const char_type* str) noexcept; + + /// \brief Constructs a basic_string_view from an ansi string of a given + /// size + /// + /// \param str the string to view + /// \param count the size of the string + constexpr basic_string_view(const char_type* str, size_type count) noexcept; + + //------------------------------------------------------------------------ + // Assignment + //------------------------------------------------------------------------ + public: + /// \brief Assigns a basic_string_view from an ansi-string + /// + /// \param view the string to view + /// \return reference to \c (*this) + basic_string_view& operator=(const basic_string_view& view) = default; + + //------------------------------------------------------------------------ + // Capacity + //------------------------------------------------------------------------ + public: + /// \brief Returns the length of the string, in terms of bytes + /// + /// \return the length of the string, in terms of bytes + constexpr size_type size() const noexcept; + + /// \copydoc basic_string_view::size + constexpr size_type length() const noexcept; + + /// \brief The largest possible number of char-like objects that can be + /// referred to by a basic_string_view. + /// \return Maximum number of characters + constexpr size_type max_size() const noexcept; + + /// \brief Returns whether the basic_string_view is empty + /// (i.e. whether its length is 0). + /// + /// \return whether the basic_string_view is empty + constexpr bool empty() const noexcept; + + //------------------------------------------------------------------------ + // Element Access + //------------------------------------------------------------------------ + public: + /// \brief Gets the ansi-string of the current basic_string_view + /// + /// \return the ansi-string pointer + constexpr const char_type* c_str() const noexcept; + + /// \brief Gets the data of the current basic_string_view + /// + /// \note This is an alias of #c_str + /// + /// \return the data this basic_string_view contains + constexpr const char_type* data() const noexcept; + + /// \brief Accesses the element at index \p pos + /// + /// \param pos the index to access + /// \return const reference to the character + constexpr const_reference operator[](size_type pos) const noexcept; + + /// \brief Accesses the element at index \p pos + /// + /// \param pos the index to access + /// \return const reference to the character + constexpr const_reference at(size_type pos) const; + + /// \brief Access the first character of the string + /// + /// \note Undefined behavior if basic_string_view is empty + /// + /// \return reference to the first character of the string + constexpr const_reference front() const noexcept; + + /// \brief References the last character of the string + /// + /// \note Undefined behavior if basic_string_view is empty + /// + /// \return reference to the last character of the string + constexpr const_reference back() const noexcept; + + //------------------------------------------------------------------------ + // Modifiers + //------------------------------------------------------------------------ + public: + /// \brief Moves the start of the view forward by n characters. + /// + /// The behavior is undefined if n > size(). + /// + /// \param n number of characters to remove from the start of the view + void remove_prefix(size_type n) noexcept; + + /// \brief Moves the end of the view back by n characters. + /// + /// The behavior is undefined if n > size(). + /// + /// \param n number of characters to remove from the end of the view + void remove_suffix(size_type n) noexcept; + + /// \brief Exchanges the view with that of v. + /// + /// \param v view to swap with + void swap(basic_string_view& v) noexcept; + + //------------------------------------------------------------------------ + // Conversions + //------------------------------------------------------------------------ + public: + /// \brief Creates a basic_string with a copy of the content of the current + /// view. + /// + /// \tparam Allocator type used to allocate internal storage + /// \param a Allocator instance to use for allocating the new string + /// + /// \return A basic_string containing a copy of the characters of the + /// current view. + template > + constexpr std::basic_string to_string( + const Allocator& a = Allocator()) const; + + /// \copydoc basic_string_view::to_string + template + explicit constexpr operator std::basic_string() + const; + + //------------------------------------------------------------------------ + // Operations + //------------------------------------------------------------------------ + public: + /// \brief Copies the substring [pos, pos + rcount) to the character string + /// pointed + /// to by dest, where rcount is the smaller of count and size() - + /// pos. + /// + /// \param dest pointer to the destination character string + /// \param count requested substring length + /// \param pos position of the first character + size_type copy(char_type* dest, + size_type count = npos, + size_type pos = 0) const; + + /// \brief Returns a substring of this viewed string + /// + /// \param pos the position of the first character in the substring + /// \param len the length of the substring + /// \return the created substring + basic_string_view substr(size_t pos = 0, size_t len = npos) const; + + //------------------------------------------------------------------------ + + /// \brief Compares two character sequences + /// + /// \param v view to compare + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(basic_string_view v) const noexcept; + + /// \brief Compares two character sequences + /// + /// \param pos position of the first character in this view to compare + /// \param count number of characters of this view to compare + /// \param v view to compare + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(size_type pos, size_type count, basic_string_view v) const; + + /// \brief Compares two character sequences + /// + /// \param pos1 position of the first character in this view to compare + /// \param count1 number of characters of this view to compare + /// \param v view to compare + /// \param pos2 position of the second character in this view to compare + /// \param count2 number of characters of the given view to compare + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(size_type pos1, + size_type count1, + basic_string_view v, + size_type pos2, + size_type count2) const; + + /// \brief Compares two character sequences + /// + /// \param s pointer to the character string to compare to + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(const char_type* s) const; + + /// \brief Compares two character sequences + /// + /// \param pos position of the first character in this view to compare + /// \param count number of characters of this view to compare + /// \param s pointer to the character string to compare to + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(size_type pos, size_type count, const char_type* s) const; + + /// \brief Compares two character sequences + /// + /// \param pos position of the first character in this view to compare + /// \param count1 number of characters of this view to compare + /// \param s pointer to the character string to compare to + /// \param count2 number of characters of the given view to compare + /// \return negative value if this view is less than the other character + /// sequence, zero if the both character sequences are equal, + /// positive value if this view is greater than the other character + /// sequence. + int compare(size_type pos, + size_type count1, + const char_type* s, + size_type count2) const; + + //------------------------------------------------------------------------ + + size_type find(basic_string_view v, size_type pos = 0) const; + + size_type find(char_type c, size_type pos = 0) const; + + size_type find(const char_type* s, size_type pos, size_type count) const; + + size_type find(const char_type* s, size_type pos = 0) const; + + //------------------------------------------------------------------------ + + size_type rfind(basic_string_view v, size_type pos = npos) const; + + size_type rfind(char_type c, size_type pos = npos) const; + + size_type rfind(const char_type* s, size_type pos, size_type count) const; + + size_type rfind(const char_type* s, size_type pos = npos) const; + + //------------------------------------------------------------------------ + + size_type find_first_of(basic_string_view v, size_type pos = 0) const; + + size_type find_first_of(char_type c, size_type pos = 0) const; + + size_type find_first_of(const char_type* s, + size_type pos, + size_type count) const; + + size_type find_first_of(const char_type* s, size_type pos = 0) const; + + //------------------------------------------------------------------------ + + size_type find_last_of(basic_string_view v, size_type pos = npos) const; + + size_type find_last_of(char_type c, size_type pos = npos) const; + + size_type find_last_of(const char_type* s, + size_type pos, + size_type count) const; + + size_type find_last_of(const char_type* s, size_type pos = npos) const; + + //------------------------------------------------------------------------ + + size_type find_first_not_of(basic_string_view v, size_type pos = 0) const; + + size_type find_first_not_of(char_type c, size_type pos = 0) const; + + size_type find_first_not_of(const char_type* s, + size_type pos, + size_type count) const; + + size_type find_first_not_of(const char_type* s, size_type pos = 0) const; + + //------------------------------------------------------------------------ + + size_type find_last_not_of(basic_string_view v, size_type pos = npos) const; + + size_type find_last_not_of(char_type c, size_type pos = npos) const; + + size_type find_last_not_of(const char_type* s, + size_type pos, + size_type count) const; + + size_type find_last_not_of(const char_type* s, size_type pos = npos) const; + + //------------------------------------------------------------------------ + // Iterators + //------------------------------------------------------------------------ + public: + /// \{ + /// \brief Retrieves the begin iterator for this basic_string_view + /// + /// \return the begin iterator + const_iterator begin() const noexcept; + const_iterator cbegin() const noexcept; + /// \} + + /// \{ + /// \brief Retrieves the end iterator for this basic_string_view + /// + /// \return the end iterator + const_iterator end() const noexcept; + const_iterator cend() const noexcept; + /// \} + + /// \{ + /// \brief Retrieves the reverse begin iterator for this basic_string_view + /// + /// \return the reverse begin iterator + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator rend() const noexcept; + /// \} + + /// \{ + /// \brief Retrieves the reverse end iterator for this basic_string_view + /// + /// \return the reverse end iterator + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + /// \} + + //------------------------------------------------------------------------ + // Private Member + //------------------------------------------------------------------------ + private: + const char_type* m_str; ///< The internal string type + size_type m_size; ///< The size of this string + + /// \brief Checks whether \p c is one of the characters in \p str + /// + /// \param c the character to check + /// \param str the characters to compare against + /// \return true if \p c is one of the characters in \p str + static bool is_one_of(CharT c, basic_string_view str); + }; + + template + const typename basic_string_view::size_type + basic_string_view::npos; + + //-------------------------------------------------------------------------- + // Public Functions + //-------------------------------------------------------------------------- + + /// \brief Overload for ostream output of basic_string_view + /// + /// \param o The output stream to print to + /// \param str the string to print + /// \return reference to the output stream + template + std::basic_ostream& operator<<( + std::basic_ostream& o, + const basic_string_view& str); + + template + void swap(basic_string_view& lhs, + basic_string_view& rhs) noexcept; + + //-------------------------------------------------------------------------- + // Comparison Functions + //-------------------------------------------------------------------------- + + template + bool operator==(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + template + bool operator!=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + template + bool operator<(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + template + bool operator>(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + template + bool operator<=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + template + bool operator>=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept; + + //-------------------------------------------------------------------------- + // Type Aliases + //-------------------------------------------------------------------------- + + using string_view = basic_string_view; + using wstring_view = basic_string_view; + using u16string_view = basic_string_view; + using u32string_view = basic_string_view; + +} // namespace Helpers + +#ifndef DETAIL_STRING_VIEW_INL +#define DETAIL_STRING_VIEW_INL + +namespace Helpers { + + //-------------------------------------------------------------------------- + // Constructor + //-------------------------------------------------------------------------- + + template + inline constexpr basic_string_view::basic_string_view() noexcept + : m_str(nullptr), m_size(0) {} + + template + template + inline basic_string_view::basic_string_view( + const std::basic_string& str) noexcept + : m_str(str.c_str()), m_size(str.size()) {} + + template + inline constexpr basic_string_view::basic_string_view( + const char_type* str) noexcept + : m_str(str), m_size(traits_type::length(str)) {} + + template + inline constexpr basic_string_view::basic_string_view( + const char_type* str, + size_type count) noexcept + : m_str(str), m_size(count) {} + + //-------------------------------------------------------------------------- + // Capacity + //-------------------------------------------------------------------------- + + template + inline constexpr typename basic_string_view::size_type + basic_string_view::size() const noexcept { + return m_size; + } + + template + inline constexpr typename basic_string_view::size_type + basic_string_view::length() const noexcept { + return size(); + } + + template + inline constexpr typename basic_string_view::size_type + basic_string_view::max_size() const noexcept { + return npos - 1; + } + + template + inline constexpr bool basic_string_view::empty() + const noexcept { + return m_size == 0; + } + + //-------------------------------------------------------------------------- + // Element Access + //-------------------------------------------------------------------------- + + template + inline constexpr const typename basic_string_view::char_type* + basic_string_view::c_str() const noexcept { + return m_str; + } + + template + inline constexpr const typename basic_string_view::char_type* + basic_string_view::data() const noexcept { + return m_str; + } + + template + inline constexpr typename basic_string_view::const_reference + basic_string_view::operator[](size_type pos) const noexcept { + return m_str[pos]; + } + + template + inline constexpr typename basic_string_view::const_reference + basic_string_view::at(size_type pos) const { + return pos < m_size ? m_str[pos] + : throw std::out_of_range( + "Input out of range in basic_string_view::at"), + m_str[pos]; + } + + template + inline constexpr typename basic_string_view::const_reference + basic_string_view::front() const noexcept { + return *m_str; + } + + template + inline constexpr typename basic_string_view::const_reference + basic_string_view::back() const noexcept { + return m_str[m_size - 1]; + } + + //-------------------------------------------------------------------------- + // Modifiers + //-------------------------------------------------------------------------- + + template + inline void basic_string_view::remove_prefix( + size_type n) noexcept { + m_str += n, m_size -= n; + } + + template + inline void basic_string_view::remove_suffix( + size_type n) noexcept { + m_size -= n; + } + + template + inline void basic_string_view::swap( + basic_string_view& v) noexcept { + using std::swap; + swap(m_size, v.m_size); + swap(m_str, v.m_str); + } + + //-------------------------------------------------------------------------- + // Conversions + //-------------------------------------------------------------------------- + + template + template + inline constexpr std::basic_string + basic_string_view::to_string(const Allocator& a) const { + return std::basic_string(m_str, m_size, a); + } + + template + template + inline constexpr basic_string_view::operator std:: + basic_string() const { + return std::basic_string(m_str, m_size); + } + + //-------------------------------------------------------------------------- + // String Operations + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::copy(char_type* dest, + size_type count, + size_type pos) const { + if (pos >= m_size) { + throw std::out_of_range("Index out of range in basic_string_view::copy"); + } + + const size_type rcount = std::min(m_size - pos, count + 1); + std::copy(m_str + pos, m_str + pos + rcount, dest); + return rcount; + } + + template + inline basic_string_view + basic_string_view::substr(size_type pos, size_type len) const { + const size_type max_length = pos > m_size ? 0 : m_size - pos; + + if (pos > size()) { + throw std::out_of_range( + "Index out of range in basic_string_view::substr"); + } + + return basic_string_view(m_str + pos, std::min(len, max_length)); + } + + //-------------------------------------------------------------------------- + + template + inline int basic_string_view::compare( + basic_string_view v) const noexcept { + const size_type rlen = std::min(m_size, v.m_size); + const int compare = Traits::compare(m_str, v.m_str, rlen); + + return (compare ? compare + : (m_size < v.m_size ? -1 : (m_size > v.m_size ? 1 : 0))); + } + + template + inline int basic_string_view::compare( + size_type pos, + size_type count, + basic_string_view v) const { + return substr(pos, count).compare(v); + } + + template + inline int basic_string_view::compare(size_type pos1, + size_type count1, + basic_string_view v, + size_type pos2, + size_type count2) const { + return substr(pos1, count1).compare(v.substr(pos2, count2)); + } + + template + inline int basic_string_view::compare( + const char_type* s) const { + return compare(basic_string_view(s)); + } + + template + inline int basic_string_view::compare( + size_type pos, + size_type count, + const char_type* s) const { + return substr(pos, count).compare(basic_string_view(s)); + } + + template + inline int basic_string_view::compare(size_type pos, + size_type count1, + const char_type* s, + size_type count2) const { + return substr(pos, count1) + .compare(basic_string_view(s, count2)); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::find(basic_string_view v, + size_type pos) const { + // Can't find a substring if the substring is bigger than this + if (pos > size()) { + return npos; + } + if ((pos + v.size()) > size()) { + return npos; + } + + const auto offset = pos; + const auto increments = size() - v.size() - offset; + + for (auto i = 0u; i <= increments; ++i) { + const auto j = i + offset; + if (substr(j, v.size()) == v) { + return j; + } + } + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::find(char_type c, size_type pos) const { + return find(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find(const char_type* s, + size_type pos, + size_type count) const { + return find(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find(const char_type* s, + size_type pos) const { + return find(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::rfind(basic_string_view v, + size_type pos) const { + if (empty()) { + return v.empty() ? 0u : npos; + } + if (v.empty()) { + return std::min(size() - 1, pos); + } + if (v.size() > size()) { + return npos; + } + + auto i = std::min(pos, (size() - v.size())); + while (i != npos) { + if (substr(i, v.size()) == v) { + return i; + } + --i; + } + + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::rfind(char_type c, size_type pos) const { + return rfind(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::rfind(const char_type* s, + size_type pos, + size_type count) const { + return rfind(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::rfind(const char_type* s, + size_type pos) const { + return rfind(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_of(basic_string_view v, + size_type pos) const { + const auto max_index = size(); + for (auto i = pos; i < max_index; ++i) { + if (is_one_of(m_str[i], v)) { + return i; + } + } + + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_of(char_type c, + size_type pos) const { + return find_first_of(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_of(const char_type* s, + size_type pos, + size_type count) const { + return find_first_of(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_of(const char_type* s, + size_type pos) const { + return find_first_of(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_of(basic_string_view v, + size_type pos) const { + if (empty()) { + return npos; + } + const auto max_index = std::min(size() - 1, pos); + for (auto i = 0u; i <= max_index; ++i) { + const auto j = max_index - i; + + if (is_one_of(m_str[j], v)) { + return j; + } + } + + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_of(char_type c, + size_type pos) const { + return find_last_of(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_of(const char_type* s, + size_type pos, + size_type count) const { + return find_last_of(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_of(const char_type* s, + size_type pos) const { + return find_last_of(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_not_of(basic_string_view v, + size_type pos) const { + const auto max_index = size(); + for (auto i = pos; i < max_index; ++i) { + if (!is_one_of(m_str[i], v)) { + return i; + } + } + + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_not_of(char_type c, + size_type pos) const { + return find_first_not_of(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_not_of(const char_type* s, + size_type pos, + size_type count) const { + return find_first_not_of(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_first_not_of(const char_type* s, + size_type pos) const { + return find_first_not_of(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_not_of(basic_string_view v, + size_type pos) const { + if (empty()) { + return npos; + } + const auto max_index = std::min(size() - 1, pos); + for (auto i = 0u; i <= max_index; ++i) { + const auto j = max_index - i; + + if (!is_one_of(m_str[j], v)) { + return j; + } + } + + return npos; + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_not_of(char_type c, + size_type pos) const { + return find_last_not_of(basic_string_view(&c, 1), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_not_of(const char_type* s, + size_type pos, + size_type count) const { + return find_last_not_of(basic_string_view(s, count), pos); + } + + template + inline typename basic_string_view::size_type + basic_string_view::find_last_not_of(const char_type* s, + size_type pos) const { + return find_last_not_of(basic_string_view(s), pos); + } + + //-------------------------------------------------------------------------- + // Iterator + //-------------------------------------------------------------------------- + + template + inline typename basic_string_view::const_iterator + basic_string_view::begin() const noexcept { + return m_str; + } + + template + inline typename basic_string_view::const_iterator + basic_string_view::cbegin() const noexcept { + return begin(); + } + + template + inline typename basic_string_view::const_iterator + basic_string_view::end() const noexcept { + return m_str + m_size; + } + + template + inline typename basic_string_view::const_iterator + basic_string_view::cend() const noexcept { + return cend(); + } + + template + inline typename basic_string_view::const_reverse_iterator + basic_string_view::rbegin() const noexcept { + return const_reverse_iterator{end()}; + } + + template + inline typename basic_string_view::const_reverse_iterator + basic_string_view::crbegin() const noexcept { + return rbegin(); + } + + template + inline typename basic_string_view::const_reverse_iterator + basic_string_view::rend() const noexcept { + return const_reverse_iterator{begin()}; + } + + template + inline typename basic_string_view::const_reverse_iterator + basic_string_view::crend() const noexcept { + return crend(); + } + + template + inline bool basic_string_view::is_one_of( + CharT c, + basic_string_view str) { + for (auto s : str) { + if (c == s) { + return true; + } + } + return false; + } + + //-------------------------------------------------------------------------- + // Public Functions + //-------------------------------------------------------------------------- + + template + std::basic_ostream& operator<<( + std::basic_ostream& o, + const basic_string_view& str) { + o.write(str.data(), str.size()); + return o; + } + + template + inline void swap(basic_string_view& lhs, + basic_string_view& rhs) noexcept { + lhs.swap(rhs); + } + + //-------------------------------------------------------------------------- + // Comparison Functions + //-------------------------------------------------------------------------- + + template + inline bool operator==(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + + template + inline bool operator==(basic_string_view lhs, + const CharT* rhs) noexcept { + return lhs == basic_string_view(rhs); + } + + template + inline bool operator==(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) == rhs; + } + + template + inline bool operator==(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) == rhs; + } + + template + inline bool operator==( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs == basic_string_view(rhs); + } + + //-------------------------------------------------------------------------- + + template + inline bool operator!=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) != 0; + } + + template + inline bool operator!=(const basic_string_view& lhs, + const CharT* rhs) noexcept { + return lhs != basic_string_view(rhs); + } + + template + inline bool operator!=(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) != rhs; + } + + template + inline bool operator!=(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) != rhs; + } + + template + inline bool operator!=( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs != basic_string_view(rhs); + } + //-------------------------------------------------------------------------- + + template + inline bool operator<(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + + template + inline bool operator<(const basic_string_view& lhs, + const CharT* rhs) noexcept { + return lhs < basic_string_view(rhs); + } + + template + inline bool operator<(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) < rhs; + } + + template + inline bool operator<(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) < rhs; + } + + template + inline bool operator<( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs < basic_string_view(rhs); + } + + //-------------------------------------------------------------------------- + + template + inline bool operator>(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + + template + inline bool operator>(const basic_string_view& lhs, + const CharT* rhs) noexcept { + return lhs > basic_string_view(rhs); + } + + template + inline bool operator>(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) > rhs; + } + + template + inline bool operator>(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) > rhs; + } + + template + inline bool operator>( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs > basic_string_view(rhs); + } + + //-------------------------------------------------------------------------- + + template + inline bool operator<=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) <= 0; + } + + template + inline bool operator<=(const basic_string_view& lhs, + const CharT* rhs) noexcept { + return lhs <= basic_string_view(rhs); + } + + template + inline bool operator<=(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) <= rhs; + } + + template + inline bool operator<=(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) <= rhs; + } + + template + inline bool operator<=( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs <= basic_string_view(rhs); + } + + //-------------------------------------------------------------------------- + + template + inline bool operator>=(const basic_string_view& lhs, + const basic_string_view& rhs) noexcept { + return lhs.compare(rhs) >= 0; + } + + template + inline bool operator>=(const basic_string_view& lhs, + const CharT* rhs) noexcept { + return lhs >= basic_string_view(rhs); + } + + template + inline bool operator>=(const CharT* lhs, + const basic_string_view& rhs) noexcept { + return basic_string_view(lhs) >= rhs; + } + + template + inline bool operator>=(const std::basic_string& lhs, + const basic_string_view& rhs) { + return basic_string_view(lhs) >= rhs; + } + + template + inline bool operator>=( + const basic_string_view& lhs, + const std::basic_string& rhs) { + return lhs >= basic_string_view(rhs); + } + +} // namespace Helpers + +#endif /* DETAIL_STRING_VIEW_INL */ + +#endif /* STRING_VIEW_HPP */ diff --git a/ESP/lib/src/io/LEDManager/LEDManager.cpp b/ESP/lib/src/io/LEDManager/LEDManager.cpp index c794556..2ff76cf 100644 --- a/ESP/lib/src/io/LEDManager/LEDManager.cpp +++ b/ESP/lib/src/io/LEDManager/LEDManager.cpp @@ -11,7 +11,13 @@ LEDManager::ledStateMap_t LEDManager::ledStateMap = { {LEDStates_e::_LedStateNone, {{0, 500}}}, - {LEDStates_e::_SerialManager_Error, + {LEDStates_e::_Improv_Error, + {{1, 1000}, {0, 500}, {0, 1000}, {0, 500}, {1, 1000}}}, + {LEDStates_e::_Improv_Start, + {{1, 500}, {0, 300}, {0, 300}, {0, 300}, {1, 500}}}, + {LEDStates_e::_Improv_Stop, + {{1, 300}, {0, 500}, {0, 500}, {0, 500}, {1, 300}}}, + {LEDStates_e::_Improv_Processing, {{1, 200}, {0, 100}, {0, 500}, {0, 100}, {1, 200}}}, {LEDStates_e::_WebServerState_Error, {{1, 200}, {0, 100}, {0, 500}, {0, 100}, {1, 200}}}, @@ -46,15 +52,14 @@ LEDManager::ledStateMap_t LEDManager::ledStateMap = { std::vector LEDManager::keepAliveStates = { LEDStates_e::_WebServerState_Error, LEDStates_e::_Camera_Error}; -LEDManager::LEDManager(byte pin, StateManager* stateManager) - : _ledPin(pin), _stateManager(stateManager), _ledState(false) {} +LEDManager::LEDManager(byte pin) : _ledPin(pin), _ledState(false) {} LEDManager::~LEDManager() {} void LEDManager::begin() { pinMode(_ledPin, OUTPUT); // the defualt state is _LedStateNone so we're fine - this->currentState = this->_stateManager->getCurrentState(); + this->currentState = ledStateManager.getCurrentState(); this->currentPatternIndex = 0; BlinkPatterns_t pattern = this->ledStateMap[this->currentState][this->currentPatternIndex]; @@ -81,7 +86,7 @@ void LEDManager::handleLED() { // start displaying it, or if we need to keep displaying the current one if (this->currentPatternIndex > this->ledStateMap[this->currentState].size() - 1) { - auto nextState = this->_stateManager->getCurrentState(); + auto nextState = ledStateManager.getCurrentState(); // we want to keep displaying the same state only if its an keepAlive one, // but we should change if the incoming one is also an errours state, maybe // more serious one this time <- this may be a bad idea @@ -98,14 +103,14 @@ void LEDManager::handleLED() { BlinkPatterns_t pattern = this->ledStateMap[this->currentState][this->currentPatternIndex]; this->nextStateChangeMillis = millis() + pattern.delayTime; - return; + return; } // it wasn't a keepAlive state, nor did we have another one ready, // we're done for now this->toggleLED(false); return; } - // we can safely adnace it and display the next stage + // we can safely advance it and display the next stage BlinkPatterns_t pattern = this->ledStateMap[this->currentState][this->currentPatternIndex]; this->toggleLED(pattern.state); diff --git a/ESP/lib/src/io/LEDManager/LEDManager.hpp b/ESP/lib/src/io/LEDManager/LEDManager.hpp index 2091ea7..f8772a3 100644 --- a/ESP/lib/src/io/LEDManager/LEDManager.hpp +++ b/ESP/lib/src/io/LEDManager/LEDManager.hpp @@ -8,7 +8,7 @@ class LEDManager { public: - LEDManager(byte pin, StateManager *stateManager); + LEDManager(byte pin); virtual ~LEDManager(); void begin(); @@ -17,7 +17,6 @@ public: private: byte _ledPin; - StateManager *_stateManager; unsigned long nextStateChangeMillis = 0; bool _ledState; @@ -34,4 +33,4 @@ private: unsigned int currentPatternIndex = 0; }; -#endif // LEDMANAGER_HPP \ No newline at end of file +#endif // LEDMANAGER_HPP diff --git a/ESP/lib/src/io/camera/cameraHandler.cpp b/ESP/lib/src/io/camera/cameraHandler.cpp index efc11ec..68249e8 100644 --- a/ESP/lib/src/io/camera/cameraHandler.cpp +++ b/ESP/lib/src/io/camera/cameraHandler.cpp @@ -1,212 +1,221 @@ #include "cameraHandler.hpp" -CameraHandler::CameraHandler(ProjectConfig *configManager, - StateManager *stateManager) : configManager(configManager), - stateManager(stateManager) {} +CameraHandler::CameraHandler(ProjectConfig& configManager) + : configManager(configManager) {} -void CameraHandler::setupCameraPinout() -{ - // Workaround for espM5SStack not having a defined camera +void CameraHandler::setupCameraPinout() { + // Workaround for espM5SStack not having a defined camera #ifdef CAMERA_MODULE_NAME - log_i("Camera module is %s", CAMERA_MODULE_NAME); + log_i("Camera module is %s", CAMERA_MODULE_NAME); #else - log_i("Camera module is undefined"); + log_i("Camera module is undefined"); #endif #if CONFIG_CAMERA_MODULE_ESP_EYE - /* IO13, IO14 is designed for JTAG by default, - * to use it as generalized input, - * firstly declair it as pullup input - **/ - pinMode(13, INPUT_PULLUP); - pinMode(14, INPUT_PULLUP); - log_i("ESP_EYE"); + /* IO13, IO14 is designed for JTAG by default, + * to use it as generalized input, + * firstly declair it as pullup input + **/ + pinMode(13, INPUT_PULLUP); + pinMode(14, INPUT_PULLUP); + log_i("ESP_EYE"); #elif CONFIG_CAMERA_MODULE_CAM_BOARD - /* IO13, IO14 is designed for JTAG by default, - * to use it as generalized input, - * firstly declair it as pullup input - **/ - pinMode(13, INPUT_PULLUP); - pinMode(14, INPUT_PULLUP); - log_i("CAM_BOARD"); + /* IO13, IO14 is designed for JTAG by default, + * to use it as generalized input, + * firstly declair it as pullup input + **/ + pinMode(13, INPUT_PULLUP); + pinMode(14, INPUT_PULLUP); + log_i("CAM_BOARD"); #endif - config.ledc_channel = LEDC_CHANNEL_0; - config.ledc_timer = LEDC_TIMER_0; - config.grab_mode = CAMERA_GRAB_LATEST; - config.pin_d0 = Y2_GPIO_NUM; - config.pin_d1 = Y3_GPIO_NUM; - config.pin_d2 = Y4_GPIO_NUM; - config.pin_d3 = Y5_GPIO_NUM; - config.pin_d4 = Y6_GPIO_NUM; - config.pin_d5 = Y7_GPIO_NUM; - config.pin_d6 = Y8_GPIO_NUM; - config.pin_d7 = Y9_GPIO_NUM; - config.pin_xclk = XCLK_GPIO_NUM; - config.pin_pclk = PCLK_GPIO_NUM; - config.pin_vsync = VSYNC_GPIO_NUM; - config.pin_href = HREF_GPIO_NUM; - config.pin_sccb_sda = SIOD_GPIO_NUM; - config.pin_sccb_scl = SIOC_GPIO_NUM; - config.pin_pwdn = PWDN_GPIO_NUM; - config.pin_reset = RESET_GPIO_NUM; - config.xclk_freq_hz = 16500000; // 10000000 stable, - // 16500000 optimal, - // 20000000 max fps + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.grab_mode = CAMERA_GRAB_LATEST; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sccb_sda = SIOD_GPIO_NUM; + config.pin_sccb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 16500000; // 10000000 stable, + // 16500000 optimal, + // 20000000 max fps } -void CameraHandler::setupBasicResolution() -{ - config.pixel_format = PIXFORMAT_JPEG; - config.frame_size = FRAMESIZE_240X240; +void CameraHandler::setupBasicResolution() { + config.pixel_format = PIXFORMAT_JPEG; + config.frame_size = FRAMESIZE_240X240; - if (!psramFound()) - { - log_e("Did not find psram, setting lower image quality"); - config.fb_location = CAMERA_FB_IN_DRAM; - config.jpeg_quality = 9; - config.fb_count = 2; - return; - } + if (!psramFound()) { + log_e("Did not find psram, setting lower image quality"); + config.fb_location = CAMERA_FB_IN_DRAM; + config.jpeg_quality = 9; + config.fb_count = 2; + return; + } - log_d("Found psram, setting the higher image quality"); - config.jpeg_quality = 7; // 0-63 lower number = higher quality, more latency and less fps 7 for most fps, 5 for best quality - config.fb_count = 3; + log_d("Found psram, setting the higher image quality"); + config.jpeg_quality = 7; // 0-63 lower number = higher quality, more latency + // and less fps 7 for most fps, 5 for best quality + config.fb_count = 3; } -void CameraHandler::setupCameraSensor() -{ - camera_sensor = esp_camera_sensor_get(); - // fixes corrupted jpegs, https://github.com/espressif/esp32-camera/issues/203 - // documentation https://www.uctronics.com/download/cam_module/OV2640DS.pdf - camera_sensor->set_reg(camera_sensor, 0xff, 0xff, 0x00); // banksel, here we're directly writing to the registers. 0xFF==0x00 is the first bank, there's also 0xFF==0x01 - camera_sensor->set_reg(camera_sensor, 0xd3, 0xff, 5); // clock - camera_sensor->set_brightness(camera_sensor, 2); // -2 to 2 - camera_sensor->set_contrast(camera_sensor, 2); // -2 to 2 - camera_sensor->set_saturation(camera_sensor, -2); // -2 to 2 +void CameraHandler::setupCameraSensor() { + camera_sensor = esp_camera_sensor_get(); + // fixes corrupted jpegs, https://github.com/espressif/esp32-camera/issues/203 + // documentation https://www.uctronics.com/download/cam_module/OV2640DS.pdf + camera_sensor->set_reg( + camera_sensor, 0xff, 0xff, + 0x00); // banksel, here we're directly writing to the registers. + // 0xFF==0x00 is the first bank, there's also 0xFF==0x01 + camera_sensor->set_reg(camera_sensor, 0xd3, 0xff, 5); // clock + camera_sensor->set_brightness(camera_sensor, 2); // -2 to 2 + camera_sensor->set_contrast(camera_sensor, 2); // -2 to 2 + camera_sensor->set_saturation(camera_sensor, -2); // -2 to 2 - // white balance control - camera_sensor->set_whitebal(camera_sensor, 1); // 0 = disable , 1 = enable - camera_sensor->set_awb_gain(camera_sensor, 0); // 0 = disable , 1 = enable - camera_sensor->set_wb_mode(camera_sensor, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - - // controls the exposure - camera_sensor->set_exposure_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable - camera_sensor->set_aec2(camera_sensor, 0); // 0 = disable , 1 = enable - camera_sensor->set_ae_level(camera_sensor, 0); // -2 to 2 - camera_sensor->set_aec_value(camera_sensor, 300); // 0 to 1200 + // white balance control + camera_sensor->set_whitebal(camera_sensor, 1); // 0 = disable , 1 = enable + camera_sensor->set_awb_gain(camera_sensor, 0); // 0 = disable , 1 = enable + camera_sensor->set_wb_mode(camera_sensor, + 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - + // Sunny, 2 - Cloudy, 3 - Office, 4 - Home) - // controls the gain - camera_sensor->set_gain_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable - - // automatic gain control gain, controls by how much the resulting image should be amplified - camera_sensor->set_agc_gain(camera_sensor, 2); // 0 to 30 - camera_sensor->set_gainceiling(camera_sensor, (gainceiling_t)6); // 0 to 6 + // controls the exposure + camera_sensor->set_exposure_ctrl(camera_sensor, + 0); // 0 = disable , 1 = enable + camera_sensor->set_aec2(camera_sensor, 0); // 0 = disable , 1 = enable + camera_sensor->set_ae_level(camera_sensor, 0); // -2 to 2 + camera_sensor->set_aec_value(camera_sensor, 300); // 0 to 1200 - // black and white pixel correction, averages the white and black spots - camera_sensor->set_bpc(camera_sensor, 1); // 0 = disable , 1 = enable - camera_sensor->set_wpc(camera_sensor, 1); // 0 = disable , 1 = enable - // digital clamp white balance - camera_sensor->set_dcw(camera_sensor, 0); // 0 = disable , 1 = enable + // controls the gain + camera_sensor->set_gain_ctrl(camera_sensor, 0); // 0 = disable , 1 = enable - //gamma correction - camera_sensor->set_raw_gma(camera_sensor, 1); // 0 = disable , 1 = enable (makes much lighter and noisy) - - camera_sensor->set_lenc(camera_sensor, 0); // 0 = disable , 1 = enable // 0 = disable , 1 = enable - - camera_sensor->set_colorbar(camera_sensor, 0); // 0 = disable , 1 = enable - - camera_sensor->set_special_effect(camera_sensor, 2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) + // automatic gain control gain, controls by how much the resulting image + // should be amplified + camera_sensor->set_agc_gain(camera_sensor, 2); // 0 to 30 + camera_sensor->set_gainceiling(camera_sensor, (gainceiling_t)6); // 0 to 6 + + // black and white pixel correction, averages the white and black spots + camera_sensor->set_bpc(camera_sensor, 1); // 0 = disable , 1 = enable + camera_sensor->set_wpc(camera_sensor, 1); // 0 = disable , 1 = enable + // digital clamp white balance + camera_sensor->set_dcw(camera_sensor, 0); // 0 = disable , 1 = enable + + // gamma correction + camera_sensor->set_raw_gma( + camera_sensor, + 1); // 0 = disable , 1 = enable (makes much lighter and noisy) + + camera_sensor->set_lenc(camera_sensor, 0); // 0 = disable , 1 = enable // 0 = + // disable , 1 = enable + + camera_sensor->set_colorbar(camera_sensor, 0); // 0 = disable , 1 = enable + + camera_sensor->set_special_effect( + camera_sensor, + 2); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, + // 4 - Green Tint, 5 - Blue Tint, 6 - Sepia) } -bool CameraHandler::setupCamera() -{ - this->setupCameraPinout(); - this->setupBasicResolution(); - esp_err_t hasCameraBeenInitialized = esp_camera_init(&config); +bool CameraHandler::setupCamera() { + this->setupCameraPinout(); + this->setupBasicResolution(); + esp_err_t hasCameraBeenInitialized = esp_camera_init(&config); - if (hasCameraBeenInitialized != ESP_OK) - { - log_e("Camera initialization failed with error: 0x%x \r\n", hasCameraBeenInitialized); - log_e("Camera most likely not seated properly in the socket. Please fix the camera and reboot the device.\r\n"); - stateManager->setState(LEDStates_e::_Camera_Error); - return false; - } + if (hasCameraBeenInitialized != ESP_OK) { + log_e("Camera initialization failed with error: 0x%x \r\n", + hasCameraBeenInitialized); + log_e( + "Camera most likely not seated properly in the socket. Please fix the " + "camera and reboot the device.\r\n"); + ledStateManager.setState(LEDStates_e::_Camera_Error); + return false; + } - this->setupCameraSensor(); - return true; + this->setupCameraSensor(); + return true; } -void CameraHandler::loadConfigData() -{ - ProjectConfig::CameraConfig_t *cameraConfig = configManager->getCameraConfig(); - this->setHFlip(cameraConfig->href); - this->setVFlip(cameraConfig->vflip); - this->setCameraResolution((framesize_t)cameraConfig->framesize); - camera_sensor->set_quality(camera_sensor, cameraConfig->quality); - camera_sensor->set_agc_gain(camera_sensor, cameraConfig->brightness); +void CameraHandler::loadConfigData() { + ProjectConfig::CameraConfig_t cameraConfig = configManager.getCameraConfig(); + this->setHFlip(cameraConfig.href); + this->setVFlip(cameraConfig.vflip); + this->setCameraResolution((framesize_t)cameraConfig.framesize); + camera_sensor->set_quality(camera_sensor, cameraConfig.quality); + camera_sensor->set_agc_gain(camera_sensor, cameraConfig.brightness); } -void CameraHandler::update(ObserverEvent::Event event) -{ - switch (event) - { - case ObserverEvent::Event::configLoaded: - this->setupCamera(); - this->loadConfigData(); - break; - case ObserverEvent::Event::cameraConfigUpdated: - this->loadConfigData(); - break; - default: - break; - } +int CameraHandler::setCameraResolution(framesize_t frameSize) { + if (camera_sensor->pixformat == PIXFORMAT_JPEG) { + try { + return camera_sensor->set_framesize(camera_sensor, frameSize); + } catch (...) { + // they sent us a malformed or unsupported frameSize - rather than crash - + // tell them about it + return -1; + } + } + return -1; } -int CameraHandler::setCameraResolution(framesize_t frameSize) -{ - if (camera_sensor->pixformat == PIXFORMAT_JPEG) - { - try - { - return camera_sensor->set_framesize(camera_sensor, frameSize); - } - catch (...) - { - // they sent us a malformed or unsupported frameSize - rather than crash - tell them about it - return -1; - } - } - return -1; +int CameraHandler::setVFlip(int direction) { + return camera_sensor->set_vflip(camera_sensor, direction); } -int CameraHandler::setVFlip(int direction) -{ - return camera_sensor->set_vflip(camera_sensor, direction); +int CameraHandler::setHFlip(int direction) { + return camera_sensor->set_hmirror(camera_sensor, direction); } -int CameraHandler::setHFlip(int direction) -{ - return camera_sensor->set_hmirror(camera_sensor, direction); +int CameraHandler::setVieWindow(int offsetX, + int offsetY, + int outputX, + int outputY) { + return 0; } //! either hardware(1) or software(0) -void CameraHandler::resetCamera(bool type) -{ - if (type) - { - // power cycle the camera module (handy if camera stops responding) - digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module - Network_Utilities::my_delay(0.3); // a for loop with a delay of 300ms - digitalWrite(PWDN_GPIO_NUM, LOW); - Network_Utilities::my_delay(0.3); - setupCamera(); - } - else - { - // reset via software (handy if you wish to change resolution or image type etc. - see test procedure) - esp_camera_deinit(); - Network_Utilities::my_delay(0.05); - setupCamera(); - } +void CameraHandler::resetCamera(bool type) { + if (type) { + // power cycle the camera module (handy if camera stops responding) + digitalWrite(PWDN_GPIO_NUM, HIGH); // turn power off to camera module + Network_Utilities::my_delay(0.3); // a for loop with a delay of 300ms + digitalWrite(PWDN_GPIO_NUM, LOW); + Network_Utilities::my_delay(0.3); + setupCamera(); + } else { + // reset via software (handy if you wish to change resolution or image type + // etc. - see test procedure) + esp_camera_deinit(); + Network_Utilities::my_delay(0.05); + setupCamera(); + } +} + +void CameraHandler::update(ConfigState_e event) { + switch (event) { + case ConfigState_e::configLoaded: + this->setupCamera(); + this->loadConfigData(); + break; + case ConfigState_e::cameraConfigUpdated: + this->loadConfigData(); + break; + default: + break; + } +} + +std::string CameraHandler::getName() { + return "CameraHandler"; } diff --git a/ESP/lib/src/io/camera/cameraHandler.hpp b/ESP/lib/src/io/camera/cameraHandler.hpp index ffd2d93..93250fb 100644 --- a/ESP/lib/src/io/camera/cameraHandler.hpp +++ b/ESP/lib/src/io/camera/cameraHandler.hpp @@ -6,27 +6,26 @@ #include "data/config/project_config.hpp" #include "data/StateManager/StateManager.hpp" -class CameraHandler : public IObserver -{ -private: - sensor_t *camera_sensor; - camera_config_t config; - ProjectConfig *configManager; - StateManager *stateManager; +class CameraHandler : public IObserver { + private: + sensor_t* camera_sensor; + camera_config_t config; + ProjectConfig& configManager; -public: - CameraHandler(ProjectConfig *configManager, StateManager *stateManager); - int setCameraResolution(framesize_t frameSize); - int setVFlip(int direction); - int setHFlip(int direction); - int setVieWindow(int offsetX, int offsetY, int outputX, int outputY); - void update(ObserverEvent::Event event); - void resetCamera(bool type = 0); + public: + CameraHandler(ProjectConfig& configManager); + int setCameraResolution(framesize_t frameSize); + int setVFlip(int direction); + int setHFlip(int direction); + int setVieWindow(int offsetX, int offsetY, int outputX, int outputY); + void update(ConfigState_e event); + std::string getName(); + void resetCamera(bool type = 0); -private: - void loadConfigData(); - bool setupCamera(); - void setupCameraPinout(); - void setupBasicResolution(); - void setupCameraSensor(); + private: + void loadConfigData(); + bool setupCamera(); + void setupCameraPinout(); + void setupBasicResolution(); + void setupCameraSensor(); }; diff --git a/ESP/lib/src/network/WifiHandler/WifiHandler.hpp b/ESP/lib/src/network/WifiHandler/WifiHandler.hpp deleted file mode 100644 index 33a0aa7..0000000 --- a/ESP/lib/src/network/WifiHandler/WifiHandler.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#ifndef WIFIHANDLER_HPP -#define WIFIHANDLER_HPP -#include -#include -#include -#include -#include "data/StateManager/StateManager.hpp" -#include "data/config/project_config.hpp" -#include "data/utilities/helpers.hpp" - -class WiFiHandler -{ -public: - WiFiHandler(ProjectConfig *configManager, - StateManager *stateManager, - StateManager *ledStateManger, - const std::string &ssid, - const std::string &password, - uint8_t channel); - virtual ~WiFiHandler(); - void setupWifi(); - - ProjectConfig *configManager; - StateManager *stateManager; - StateManager *ledStateManager; - - bool _enable_adhoc; - -private: - void setUpADHOC(); - void adhoc(const std::string &ssid, uint8_t channel, const std::string &password = std::string()); - bool iniSTA(const std::string &ssid, const std::string &password, uint8_t channel, wifi_power_t power); - - std::string ssid; - std::string password; - uint8_t channel; - uint8_t power; -}; -#endif // WIFIHANDLER_HPP diff --git a/ESP/lib/src/network/WifiHandler/wifiHandler.cpp b/ESP/lib/src/network/WifiHandler/wifihandler.cpp similarity index 57% rename from ESP/lib/src/network/WifiHandler/wifiHandler.cpp rename to ESP/lib/src/network/WifiHandler/wifihandler.cpp index 5f37aa4..0c191f8 100644 --- a/ESP/lib/src/network/WifiHandler/wifiHandler.cpp +++ b/ESP/lib/src/network/WifiHandler/wifihandler.cpp @@ -1,14 +1,13 @@ -#include "WifiHandler.hpp" +#include "wifihandler.hpp" +#include +#include "data/StateManager/StateManager.hpp" +#include "data/utilities/helpers.hpp" -WiFiHandler::WiFiHandler(ProjectConfig* configManager, - StateManager* stateManager, - StateManager* ledStateManger, +WiFiHandler::WiFiHandler(ProjectConfig& configManager, const std::string& ssid, const std::string& password, uint8_t channel) : configManager(configManager), - stateManager(stateManager), - ledStateManager(ledStateManager), ssid(ssid), password(password), channel(channel), @@ -17,41 +16,38 @@ WiFiHandler::WiFiHandler(ProjectConfig* configManager, WiFiHandler::~WiFiHandler() {} -void WiFiHandler::setupWifi() { +void WiFiHandler::begin() { if (this->_enable_adhoc || - stateManager->getCurrentState() == WiFiState_e::WiFiState_ADHOC) { + wifiStateManager.getCurrentState() == WiFiState_e::WiFiState_ADHOC) { this->setUpADHOC(); return; } - ProjectConfig::WiFiTxPower_t* txpower = configManager->getWiFiTxPowerConfig(); + auto txpower = configManager.getWiFiTxPowerConfig(); WiFi.mode(WIFI_STA); WiFi.setSleep(WIFI_PS_NONE); log_i("Initializing connection to wifi \n\r"); - stateManager->setState(WiFiState_e::WiFiState_Connecting); + wifiStateManager.setState(WiFiState_e::WiFiState_Connecting); - std::vector* networks = - configManager->getWifiConfigs(); + auto networks = configManager.getWifiConfigs(); - if (networks->empty()) { + if (networks.empty()) { log_i("No networks found in config, trying the default one \n\r"); if (this->iniSTA(this->ssid, this->password, this->channel, - (wifi_power_t)txpower->power)) { - return; - } else { - log_i( - "Could not connect to the hardcoded network, setting up ADHOC " - "network \n\r"); - this->setUpADHOC(); + (wifi_power_t)txpower.power)) { return; } + log_i( + "Could not connect to the hardcoded network, setting up ADHOC " + "network \n\r"); + this->setUpADHOC(); + return; } - for (auto networkIterator = networks->begin(); - networkIterator != networks->end(); ++networkIterator) { - if (this->iniSTA(networkIterator->ssid, networkIterator->password, - networkIterator->channel, - (wifi_power_t)networkIterator->power)) { + for (auto& network : networks) { + log_i("Trying to connect to network: %s \n\r", network.ssid.c_str()); + if (this->iniSTA(network.ssid, network.password, network.channel, + (wifi_power_t)txpower.power)) { return; } } @@ -62,20 +58,21 @@ void WiFiHandler::setupWifi() { "to hardcoded network: %s \n\r", this->ssid.c_str()); if (this->iniSTA(this->ssid, this->password, this->channel, - (wifi_power_t)txpower->power)) { + (wifi_power_t)txpower.power)) { log_i("Successfully connected to the hardcoded network. \n\r"); - } else { - log_i( - "Could not connect to the hardcoded network, setting up adhoc. " - "\n\r"); - this->setUpADHOC(); + return; } + + log_i( + "Could not connect to the hardcoded network, setting up adhoc. " + "\n\r"); + this->setUpADHOC(); } void WiFiHandler::adhoc(const std::string& ssid, uint8_t channel, const std::string& password) { - stateManager->setState(WiFiState_e::WiFiState_ADHOC); + wifiStateManager.setState(WiFiState_e::WiFiState_ADHOC); log_i("\n[INFO]: Configuring access point...\n"); WiFi.mode(WIFI_AP); @@ -84,16 +81,16 @@ void WiFiHandler::adhoc(const std::string& ssid, IPAddress IP = WiFi.softAPIP(); Serial.printf("[INFO]: AP IP address: %s.\r\n", IP.toString().c_str()); // You can remove the password parameter if you want the AP to be open. - ProjectConfig::WiFiTxPower_t* txpower = configManager->getWiFiTxPowerConfig(); + ProjectConfig::WiFiTxPower_t txpower = configManager.getWiFiTxPowerConfig(); WiFi.softAP(ssid.c_str(), password.c_str(), channel); // AP mode with password - WiFi.setTxPower((wifi_power_t)txpower->power); + WiFi.setTxPower((wifi_power_t)txpower.power); } void WiFiHandler::setUpADHOC() { log_i("\n[INFO]: Setting Up Access Point...\n"); - size_t ssidLen = configManager->getAPWifiConfig()->ssid.length(); - size_t passwordLen = configManager->getAPWifiConfig()->password.length(); + size_t ssidLen = configManager.getAPWifiConfig().ssid.length(); + size_t passwordLen = configManager.getAPWifiConfig().password.length(); if (ssidLen <= 0) { log_i("\n[INFO]: Configuring access point with default values\n"); this->adhoc(WIFI_AP_SSID, WIFI_AP_CHANNEL, WIFI_AP_PASSWORD); @@ -102,21 +99,20 @@ void WiFiHandler::setUpADHOC() { if (passwordLen <= 0) { log_i("\n[INFO]: Configuring access point without a password\n"); - this->adhoc(configManager->getAPWifiConfig()->ssid, - configManager->getAPWifiConfig()->channel); + this->adhoc(configManager.getAPWifiConfig().ssid, + configManager.getAPWifiConfig().channel); return; } - this->adhoc(configManager->getAPWifiConfig()->ssid, - configManager->getAPWifiConfig()->channel, - configManager->getAPWifiConfig()->password); + this->adhoc(configManager.getAPWifiConfig().ssid, + configManager.getAPWifiConfig().channel, + configManager.getAPWifiConfig().password); log_i("\n[INFO]: Configuring access point...\n"); - log_d("\n[DEBUG]: ssid: %s\n", - configManager->getAPWifiConfig()->ssid.c_str()); + log_d("\n[DEBUG]: ssid: %s\n", configManager.getAPWifiConfig().ssid.c_str()); log_d("\n[DEBUG]: password: %s\n", - configManager->getAPWifiConfig()->password.c_str()); - log_d("\n[DEBUG]: channel: %d\n", configManager->getAPWifiConfig()->channel); + configManager.getAPWifiConfig().password.c_str()); + log_d("\n[DEBUG]: channel: %d\n", configManager.getAPWifiConfig().channel); } bool WiFiHandler::iniSTA(const std::string& ssid, @@ -128,13 +124,13 @@ bool WiFiHandler::iniSTA(const std::string& ssid, int connectionTimeout = 30000; // 30 seconds int progress = 0; - stateManager->setState(WiFiState_e::WiFiState_Connecting); + wifiStateManager.setState(WiFiState_e::WiFiState_Connecting); log_i("Trying to connect to: %s \n\r", ssid.c_str()); - auto mdnsConfig = configManager->getMDNSConfig(); + auto mdnsConfig = configManager.getMDNSConfig(); WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE); // need to call before setting hostname - WiFi.setHostname(mdnsConfig->hostname.c_str()); + WiFi.setHostname(mdnsConfig.hostname.c_str()); WiFi.begin(ssid.c_str(), password.c_str(), channel); while (WiFi.status() != WL_CONNECTED) { progress++; @@ -142,14 +138,28 @@ bool WiFiHandler::iniSTA(const std::string& ssid, Helpers::update_progress_bar(progress, 100); delay(301); if ((currentMillis - startingMillis) >= connectionTimeout) { - stateManager->setState(WiFiState_e::WiFiState_Error); + wifiStateManager.setState(WiFiState_e::WiFiState_Error); log_e("Connection to: %s TIMEOUT \n\r", ssid.c_str()); return false; } } - stateManager->setState(WiFiState_e::WiFiState_Connected); + wifiStateManager.setState(WiFiState_e::WiFiState_Connected); log_i("Successfully connected to %s \n\r", ssid.c_str()); log_i("Setting TX power to: %d \n\r", (uint8_t)power); WiFi.setTxPower(power); return true; } + +void WiFiHandler::update(ConfigState_e event) { + switch (event) { + case ConfigState_e::networksConfigUpdated: + this->begin(); + break; + default: + break; + } +} + +std::string WiFiHandler::getName() { + return "WiFiHandler"; +} diff --git a/ESP/lib/src/network/WifiHandler/wifihandler.hpp b/ESP/lib/src/network/WifiHandler/wifihandler.hpp new file mode 100644 index 0000000..11cdab0 --- /dev/null +++ b/ESP/lib/src/network/WifiHandler/wifihandler.hpp @@ -0,0 +1,39 @@ +#pragma once +#ifndef WIFIHANDLER_HPP +#define WIFIHANDLER_HPP +#include +#include "data/config/project_config.hpp" +#include "data/utilities/Observer.hpp" + +class WiFiHandler : public IObserver { + public: + WiFiHandler(ProjectConfig& configManager, + const std::string& ssid, + const std::string& password, + uint8_t channel); + virtual ~WiFiHandler(); + void begin(); + void update(ConfigState_e event) override; + std::string getName() override; + + + bool _enable_adhoc; + + private: + void setUpADHOC(); + void adhoc(const std::string& ssid, + uint8_t channel, + const std::string& password = std::string()); + bool iniSTA(const std::string& ssid, + const std::string& password, + uint8_t channel, + wifi_power_t power); + + ProjectConfig& configManager; + + std::string ssid; + std::string password; + uint8_t channel; + uint8_t power; +}; +#endif // WIFIHANDLER_HPP diff --git a/ESP/lib/src/network/api/baseAPI/baseAPI.cpp b/ESP/lib/src/network/api/baseAPI/baseAPI.cpp index 5449b33..a55ea2d 100644 --- a/ESP/lib/src/network/api/baseAPI/baseAPI.cpp +++ b/ESP/lib/src/network/api/baseAPI/baseAPI.cpp @@ -10,17 +10,19 @@ // const char *BaseAPI::MIMETYPE_ICO{"image/x-icon"}; const char* BaseAPI::MIMETYPE_JSON{"application/json"}; -BaseAPI::BaseAPI(ProjectConfig* projectConfig, - CameraHandler* camera, - StateManager* WiFiStateManager, +BaseAPI::BaseAPI(ProjectConfig& projectConfig, +#ifndef SIM_ENABLED + CameraHandler& camera, +#endif // SIM_ENABLED const std::string& api_url, - int port) - : projectConfig(projectConfig), + const int CONTROL_PORT) + : server(CONTROL_PORT), + projectConfig(projectConfig), +#ifndef SIM_ENABLED camera(camera), - wiFiStateManager(wiFiStateManager), - server(port), - api_url(api_url), - _authRequired(false) {} +#endif // SIM_ENABLED + api_url(api_url) { +} BaseAPI::~BaseAPI() {} @@ -97,13 +99,13 @@ void BaseAPI::setWiFi(AsyncWebServerRequest* request) { } // note: We're passing empty params by design, this is done to reset // specific fields - projectConfig->setWifiConfig(networkName, ssid, password, &channel, - &power, adhoc, true); + projectConfig.setWifiConfig(networkName, ssid, password, channel, power, + adhoc, true); /* if (WiFiStateManager->getCurrentState() == WiFiState_e::WiFiState_ADHOC) { - projectConfig->setAPWifiConfig(ssid, password, &channel, adhoc, + projectConfig.setAPWifiConfig(ssid, password, &channel, adhoc, true); } else @@ -116,7 +118,7 @@ void BaseAPI::setWiFi(AsyncWebServerRequest* request) { break; } case DELETE: { - projectConfig->deleteWifiConfig(request->arg("networkName").c_str(), + projectConfig.deleteWifiConfig(request->arg("networkName").c_str(), true); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Wifi Creds have been deleted.\"}"); @@ -135,22 +137,22 @@ void BaseAPI::getJsonConfig(AsyncWebServerRequest* request) { switch (_networkMethodsMap_enum[request->method()]) { case GET: { std::string wifiConfigSerialized = "\"wifi_config\": ["; - auto networksConfigs = projectConfig->getWifiConfigs(); - for (auto networkIterator = networksConfigs->begin(); - networkIterator != networksConfigs->end(); networkIterator++) { - wifiConfigSerialized += - networkIterator->toRepresentation() + - (std::next(networkIterator) != networksConfigs->end() ? "," : ""); + auto networksConfigs = projectConfig.getWifiConfigs(); + for (auto& networkConfig : networksConfigs) { + wifiConfigSerialized += networkConfig.toRepresentation(); + + if (&networkConfig != &networksConfigs.back()) + wifiConfigSerialized += ","; } wifiConfigSerialized += "]"; std::string json = Helpers::format_string( "{%s, %s, %s, %s, %s}", - projectConfig->getDeviceConfig()->toRepresentation().c_str(), - projectConfig->getCameraConfig()->toRepresentation().c_str(), + projectConfig.getDeviceConfig().toRepresentation().c_str(), + projectConfig.getCameraConfig().toRepresentation().c_str(), wifiConfigSerialized.c_str(), - projectConfig->getMDNSConfig()->toRepresentation().c_str(), - projectConfig->getAPWifiConfig()->toRepresentation().c_str()); + projectConfig.getMDNSConfig().toRepresentation().c_str(), + projectConfig.getAPWifiConfig().toRepresentation().c_str()); request->send(200, MIMETYPE_JSON, json.c_str()); break; } @@ -198,8 +200,8 @@ void BaseAPI::setDeviceConfig(AsyncWebServerRequest* request) { } // note: We're passing empty params by design, this is done to reset // specific fields - projectConfig->setDeviceConfig(ota_login, ota_password, ota_port, true); - projectConfig->setMDNSConfig(hostname, service, true); + projectConfig.setDeviceConfig(ota_login, ota_password, ota_port, true); + projectConfig.setMDNSConfig(hostname, service, true); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Device Config has been set.\"}"); } @@ -219,8 +221,8 @@ void BaseAPI::setWiFiTXPower(AsyncWebServerRequest* request) { txPower = atoi(param->value().c_str()); } } - projectConfig->setWiFiTxPower(&txPower, true); - projectConfig->wifiTxPowerConfigSave(); + projectConfig.setWiFiTxPower(txPower, true); + projectConfig.wifiTxPowerConfigSave(); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. TX Power has been set.\"}"); break; @@ -236,8 +238,8 @@ void BaseAPI::setWiFiTXPower(AsyncWebServerRequest* request) { txPower = atoi(param->value().c_str()); } } - projectConfig->setWiFiTxPower(&txPower, true); - projectConfig->wifiTxPowerConfigSave(); + projectConfig.setWiFiTxPower(txPower, true); + projectConfig.wifiTxPowerConfigSave(); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. TX Power has been set.\"}"); } @@ -261,7 +263,7 @@ void BaseAPI::factoryReset(AsyncWebServerRequest* request) { switch (_networkMethodsMap_enum[request->method()]) { case GET: { log_d("Factory Reset"); - projectConfig->reset(); + projectConfig.reset(); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Factory Reset\"}"); } default: { @@ -274,7 +276,7 @@ void BaseAPI::factoryReset(AsyncWebServerRequest* request) { //********************************************************************************************* //! Camera Command Functions //********************************************************************************************* - +#ifndef SIM_ENABLED void BaseAPI::setCamera(AsyncWebServerRequest* request) { switch (_networkMethodsMap_enum[request->method()]) { case GET: { @@ -305,9 +307,9 @@ void BaseAPI::setCamera(AsyncWebServerRequest* request) { } // note: We're passing empty params by design, this is done to reset // specific fields - projectConfig->setCameraConfig(&temp_camera_vflip, &temp_camera_framesize, - &temp_camera_hflip, &temp_camera_quality, - &temp_camera_brightness, true); + projectConfig.setCameraConfig(temp_camera_vflip, temp_camera_framesize, + temp_camera_hflip, temp_camera_quality, + temp_camera_brightness, true); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Camera Settings have been set.\"}"); @@ -323,11 +325,12 @@ void BaseAPI::setCamera(AsyncWebServerRequest* request) { void BaseAPI::restartCamera(AsyncWebServerRequest* request) { bool mode = (bool)atoi(request->arg("mode").c_str()); - camera->resetCamera(mode); + camera.resetCamera(mode); request->send(200, MIMETYPE_JSON, "{\"msg\":\"Done. Camera had been restarted.\"}"); } +#endif // SIM_ENABLED //********************************************************************************************* //! General Command Functions @@ -338,7 +341,7 @@ void BaseAPI::ping(AsyncWebServerRequest* request) { } void BaseAPI::save(AsyncWebServerRequest* request) { - projectConfig->save(); + projectConfig.save(); request->send(200, MIMETYPE_JSON, "{\"msg\": \"ok\" }"); } @@ -372,10 +375,10 @@ void BaseAPI::checkAuthentication(AsyncWebServerRequest* request, void BaseAPI::beginOTA() { // NOTE: Code adapted from: https://github.com/ayushsharma82/AsyncElegantOTA/ - auto device_config = projectConfig->getDeviceConfig(); - auto mdns_config = projectConfig->getMDNSConfig(); + auto device_config = projectConfig.getDeviceConfig(); + auto mdns_config = projectConfig.getMDNSConfig(); - if (device_config->OTAPassword.empty()) { + if (device_config.OTAPassword.empty()) { log_e( "Password is empty, you need to provide a password in order to setup " "the OTA server"); @@ -386,13 +389,13 @@ void BaseAPI::beginOTA() { log_i( "[OTA Server]: Navigate to http://%s.local:81/update to update the " "firmware", - mdns_config->hostname.c_str()); + mdns_config.hostname.c_str()); log_d("[OTA Server]: Username: %s, Password: %s", - device_config->OTALogin.c_str(), device_config->OTAPassword.c_str()); + device_config.OTALogin.c_str(), device_config.OTAPassword.c_str()); log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap()); - const char* login = device_config->OTALogin.c_str(); - const char* password = device_config->OTAPassword.c_str(); + const char* login = device_config.OTALogin.c_str(); + const char* password = device_config.OTAPassword.c_str(); log_d("[DEBUG] Free Heap: %d", ESP.getFreeHeap()); // Note: HTTP_GET diff --git a/ESP/lib/src/network/api/baseAPI/baseAPI.hpp b/ESP/lib/src/network/api/baseAPI/baseAPI.hpp index f94dfbb..4b6a544 100644 --- a/ESP/lib/src/network/api/baseAPI/baseAPI.hpp +++ b/ESP/lib/src/network/api/baseAPI/baseAPI.hpp @@ -1,6 +1,9 @@ #ifndef BASEAPI_HPP #define BASEAPI_HPP +//! Warning do not format this file with clang-format or it will break the code + +#include #include #include @@ -97,32 +100,33 @@ class BaseAPI { typedef std::unordered_map networkMethodsMap_t; - ProjectConfig* projectConfig; - /// @brief Local instance of the AsyncWebServer - so that we dont need to use - /// new and delete - CameraHandler* camera; - StateManager* wiFiStateManager; - AsyncWebServer server; - - public: - BaseAPI(ProjectConfig* projectConfig, - CameraHandler* camera, - StateManager* wiFiStateManager, - const std::string& api_url, + ProjectConfig &projectConfig; + /// @brief Local instance of the AsyncWebServer - so that we dont need to use new and delete + AsyncWebServer server; #ifndef SIM_ENABLED - int port = 81 + CameraHandler &camera; +#endif // SIM_ENABLED + +public : + BaseAPI(ProjectConfig& projectConfig, +#ifndef SIM_ENABLED + CameraHandler& camera, +#endif // SIM_ENABLED + const std::string& api_url, +#ifndef SIM_ENABLED + int port = 81 #else - int port = 80 + int port = 80 #endif ); - virtual ~BaseAPI(); - virtual void begin(); - void checkAuthentication(AsyncWebServerRequest* request, - const char* login, - const char* password); - void beginOTA(); - void notFound(AsyncWebServerRequest* request) const; + virtual ~BaseAPI(); + virtual void begin(); + void checkAuthentication(AsyncWebServerRequest* request, + const char* login, + const char* password); + void beginOTA(); + void notFound(AsyncWebServerRequest* request) const; }; #endif // BASEAPI_HPP diff --git a/ESP/lib/src/network/api/webserverHandler.cpp b/ESP/lib/src/network/api/webserverHandler.cpp index bc18746..8193083 100644 --- a/ESP/lib/src/network/api/webserverHandler.cpp +++ b/ESP/lib/src/network/api/webserverHandler.cpp @@ -4,11 +4,17 @@ //! API Server //********************************************************************************************* -APIServer::APIServer(ProjectConfig* projectConfig, - CameraHandler* camera, - StateManager* wiFiStateManager, +APIServer::APIServer(ProjectConfig& projectConfig, +#ifndef SIM_ENABLED + CameraHandler& camera, +#endif // SIM_ENABLED const std::string& api_url) - : BaseAPI(projectConfig, camera, wiFiStateManager, api_url) {} + : BaseAPI(projectConfig, +#ifndef SIM_ENABLED + camera, +#endif // SIM_ENABLED + api_url) { +} APIServer::~APIServer() {} @@ -22,16 +28,13 @@ void APIServer::setup() { "^\\%s\\/([a-zA-Z0-9]+)\\/command\\/([a-zA-Z0-9]+)$", this->api_url.c_str()); log_d("API URL: %s", buffer); - - this->server.on(buffer, 0b01111111, [&](AsyncWebServerRequest* request) { - handleRequest(request); + server.on(buffer, 0b01111111, [&](AsyncWebServerRequest* request) { + handleRequest(request); }); - - // Note: Start OTA after all routes have been added #ifndef SIM_ENABLED - //this->_authRequired = true; + //this->_authRequired = true; #endif // SIM_ENABLED - this->beginOTA(); + beginOTA(); server.begin(); } @@ -43,8 +46,10 @@ void APIServer::setupServer() { routes.emplace("getStoredConfig", &APIServer::getJsonConfig); routes.emplace("setTxPower", &APIServer::setWiFiTXPower); // Camera Routes +#ifndef SIM_ENABLED routes.emplace("setCamera", &APIServer::setCamera); routes.emplace("restartCamera", &APIServer::restartCamera); +#endif // SIM_ENABLED routes.emplace("ping", &APIServer::ping); routes.emplace("save", &APIServer::save); routes.emplace("wifiStrength", &APIServer::rssi); @@ -57,14 +62,6 @@ void APIServer::setupServer() { indexes); // add new route map to the route_map } -void APIServer::findParam(AsyncWebServerRequest* request, - const char* param, - std::string& value) { - if (request->hasParam(param)) { - value = request->getParam(param)->value().c_str(); - } -} - /** * @brief Add a command handler to the API * diff --git a/ESP/lib/src/network/api/webserverHandler.hpp b/ESP/lib/src/network/api/webserverHandler.hpp index c09d77e..e499e65 100644 --- a/ESP/lib/src/network/api/webserverHandler.hpp +++ b/ESP/lib/src/network/api/webserverHandler.hpp @@ -6,17 +6,15 @@ class APIServer : public BaseAPI { public: - APIServer(ProjectConfig* projectConfig, - CameraHandler* camera, - StateManager* wiFiStateManager, + APIServer(ProjectConfig& projectConfig, +#ifndef SIM_ENABLED + CameraHandler& camera, +#endif // SIM_ENABLED const std::string& api_url); virtual ~APIServer(); void setup(); void setupServer(); - void findParam(AsyncWebServerRequest* request, - const char* param, - std::string& value); void addRouteMap(const std::string& index, route_t route, std::vector& indexes); diff --git a/ESP/lib/src/network/mDNS/MDNSManager.cpp b/ESP/lib/src/network/mDNS/MDNSManager.cpp index cbc2472..45c75c8 100644 --- a/ESP/lib/src/network/mDNS/MDNSManager.cpp +++ b/ESP/lib/src/network/mDNS/MDNSManager.cpp @@ -1,35 +1,37 @@ #include "MDNSManager.hpp" -MDNSHandler::MDNSHandler(StateManager* stateManager, - ProjectConfig* configManager) - : stateManager(stateManager), configManager(configManager) {} +MDNSHandler::MDNSHandler(ProjectConfig& configManager) + : configManager(configManager) {} bool MDNSHandler::startMDNS() { const std::string service = "_openiristracker"; - ProjectConfig::MDNSConfig_t* mdnsConfig = configManager->getMDNSConfig(); - if (!MDNS.begin(mdnsConfig->hostname + auto mdnsConfig = configManager.getMDNSConfig(); + if (!MDNS.begin(mdnsConfig.hostname .c_str())) // lowercase only - as this will be the url { - stateManager->setState(MDNSState_e::MDNSState_Error); + mdnsStateManager.setState(MDNSState_e::MDNSState_Error); log_e("Error initializing MDNS"); return false; } - stateManager->setState(MDNSState_e::MDNSState_Starting); + mdnsStateManager.setState(MDNSState_e::MDNSState_Starting); MDNS.addService(service.c_str(), "_tcp", 80); char port[20]; //! Add service needs leading _ on ESP32 implementation for some reason //! (according to the docs) - MDNS.addServiceTxt(service.c_str(), "_tcp", "stream_port", - "80"); + MDNS.addServiceTxt(service.c_str(), "_tcp", "stream_port", "80"); log_i("MDNS initialized!"); - stateManager->setState(MDNSState_e::MDNSState_Started); + mdnsStateManager.setState(MDNSState_e::MDNSState_Started); return true; } -void MDNSHandler::update(ObserverEvent::Event event) { +std::string MDNSHandler::getName() { + return "MDNSHandler"; +} + +void MDNSHandler::update(ConfigState_e event) { switch (event) { - case ObserverEvent::Event::mdnsConfigUpdated: + case ConfigState_e::mdnsConfigUpdated: MDNS.end(); startMDNS(); break; diff --git a/ESP/lib/src/network/mDNS/MDNSManager.hpp b/ESP/lib/src/network/mDNS/MDNSManager.hpp index b71f97f..82dd8f5 100644 --- a/ESP/lib/src/network/mDNS/MDNSManager.hpp +++ b/ESP/lib/src/network/mDNS/MDNSManager.hpp @@ -2,21 +2,19 @@ #define MDNSHANDLER_HPP #include #include "data/StateManager/StateManager.hpp" +#include "data/config/project_config.hpp" #include "data/utilities/Observer.hpp" #include "data/utilities/helpers.hpp" -#include "data/config/project_config.hpp" -class MDNSHandler : public IObserver -{ -private: - StateManager *stateManager; - ProjectConfig *configManager; +class MDNSHandler : public IObserver { + private: + ProjectConfig& configManager; -public: - MDNSHandler(StateManager *stateManager, - ProjectConfig *configManager); - bool startMDNS(); - void update(ObserverEvent::Event event); + public: + MDNSHandler(ProjectConfig& configManager); + bool startMDNS(); + void update(ConfigState_e event) override; + std::string getName() override; }; -#endif // MDNSHANDLER_HPP \ No newline at end of file +#endif // MDNSHANDLER_HPP diff --git a/ESP/lib/src/network/stream/streamServer.cpp b/ESP/lib/src/network/stream/streamServer.cpp index 5c8b0d0..951379b 100644 --- a/ESP/lib/src/network/stream/streamServer.cpp +++ b/ESP/lib/src/network/stream/streamServer.cpp @@ -74,9 +74,7 @@ esp_err_t StreamHelpers::stream(httpd_req_t *req) return res; } -StreamServer::StreamServer(int STREAM_PORT, - StateManager *stateManager) : STREAM_SERVER_PORT(STREAM_PORT), - stateManager(stateManager) {} +StreamServer::StreamServer(const int STREAM_PORT) : STREAM_SERVER_PORT(STREAM_PORT) {} int StreamServer::startStreamServer() { @@ -102,7 +100,7 @@ int StreamServer::startStreamServer() { httpd_register_uri_handler(camera_stream, &stream_page); Serial.println("Stream server initialized"); - switch (stateManager->getCurrentState()) + switch (wifiStateManager.getCurrentState()) { case WiFiState_e::WiFiState_ADHOC: Serial.printf("\n\rThe stream is under: http://%s:%i\n\r", WiFi.softAPIP().toString().c_str(), this->STREAM_SERVER_PORT); diff --git a/ESP/lib/src/network/stream/streamServer.hpp b/ESP/lib/src/network/stream/streamServer.hpp index 310e007..1157313 100644 --- a/ESP/lib/src/network/stream/streamServer.hpp +++ b/ESP/lib/src/network/stream/streamServer.hpp @@ -23,11 +23,9 @@ class StreamServer private: httpd_handle_t camera_stream = nullptr; int STREAM_SERVER_PORT; - StateManager *stateManager; public: - StreamServer(int STREAM_PORT, - StateManager *stateManager); + StreamServer(const int STREAM_PORT = 80); int startStreamServer(); }; diff --git a/ESP/lib/src/network/wifihandler/wifihandler.cpp b/ESP/lib/src/network/wifihandler/wifihandler.cpp new file mode 100644 index 0000000..0c191f8 --- /dev/null +++ b/ESP/lib/src/network/wifihandler/wifihandler.cpp @@ -0,0 +1,165 @@ +#include "wifihandler.hpp" +#include +#include "data/StateManager/StateManager.hpp" +#include "data/utilities/helpers.hpp" + +WiFiHandler::WiFiHandler(ProjectConfig& configManager, + const std::string& ssid, + const std::string& password, + uint8_t channel) + : configManager(configManager), + ssid(ssid), + password(password), + channel(channel), + power(0), + _enable_adhoc(false) {} + +WiFiHandler::~WiFiHandler() {} + +void WiFiHandler::begin() { + if (this->_enable_adhoc || + wifiStateManager.getCurrentState() == WiFiState_e::WiFiState_ADHOC) { + this->setUpADHOC(); + return; + } + auto txpower = configManager.getWiFiTxPowerConfig(); + WiFi.mode(WIFI_STA); + WiFi.setSleep(WIFI_PS_NONE); + + log_i("Initializing connection to wifi \n\r"); + wifiStateManager.setState(WiFiState_e::WiFiState_Connecting); + + auto networks = configManager.getWifiConfigs(); + + if (networks.empty()) { + log_i("No networks found in config, trying the default one \n\r"); + if (this->iniSTA(this->ssid, this->password, this->channel, + (wifi_power_t)txpower.power)) { + return; + } + log_i( + "Could not connect to the hardcoded network, setting up ADHOC " + "network \n\r"); + this->setUpADHOC(); + return; + } + + for (auto& network : networks) { + log_i("Trying to connect to network: %s \n\r", network.ssid.c_str()); + if (this->iniSTA(network.ssid, network.password, network.channel, + (wifi_power_t)txpower.power)) { + return; + } + } + + // at this point, we've tried every network, let's just setup adhoc + log_i( + "We've gone through every network, each timed out. Trying to connect " + "to hardcoded network: %s \n\r", + this->ssid.c_str()); + if (this->iniSTA(this->ssid, this->password, this->channel, + (wifi_power_t)txpower.power)) { + log_i("Successfully connected to the hardcoded network. \n\r"); + return; + } + + log_i( + "Could not connect to the hardcoded network, setting up adhoc. " + "\n\r"); + this->setUpADHOC(); +} + +void WiFiHandler::adhoc(const std::string& ssid, + uint8_t channel, + const std::string& password) { + wifiStateManager.setState(WiFiState_e::WiFiState_ADHOC); + + log_i("\n[INFO]: Configuring access point...\n"); + WiFi.mode(WIFI_AP); + WiFi.setSleep(WIFI_PS_NONE); + Serial.printf("\r\nStarting AP. \r\n"); + IPAddress IP = WiFi.softAPIP(); + Serial.printf("[INFO]: AP IP address: %s.\r\n", IP.toString().c_str()); + // You can remove the password parameter if you want the AP to be open. + ProjectConfig::WiFiTxPower_t txpower = configManager.getWiFiTxPowerConfig(); + WiFi.softAP(ssid.c_str(), password.c_str(), + channel); // AP mode with password + WiFi.setTxPower((wifi_power_t)txpower.power); +} + +void WiFiHandler::setUpADHOC() { + log_i("\n[INFO]: Setting Up Access Point...\n"); + size_t ssidLen = configManager.getAPWifiConfig().ssid.length(); + size_t passwordLen = configManager.getAPWifiConfig().password.length(); + if (ssidLen <= 0) { + log_i("\n[INFO]: Configuring access point with default values\n"); + this->adhoc(WIFI_AP_SSID, WIFI_AP_CHANNEL, WIFI_AP_PASSWORD); + return; + } + + if (passwordLen <= 0) { + log_i("\n[INFO]: Configuring access point without a password\n"); + this->adhoc(configManager.getAPWifiConfig().ssid, + configManager.getAPWifiConfig().channel); + return; + } + + this->adhoc(configManager.getAPWifiConfig().ssid, + configManager.getAPWifiConfig().channel, + configManager.getAPWifiConfig().password); + + log_i("\n[INFO]: Configuring access point...\n"); + log_d("\n[DEBUG]: ssid: %s\n", configManager.getAPWifiConfig().ssid.c_str()); + log_d("\n[DEBUG]: password: %s\n", + configManager.getAPWifiConfig().password.c_str()); + log_d("\n[DEBUG]: channel: %d\n", configManager.getAPWifiConfig().channel); +} + +bool WiFiHandler::iniSTA(const std::string& ssid, + const std::string& password, + uint8_t channel, + wifi_power_t power) { + unsigned long currentMillis = millis(); + unsigned long startingMillis = currentMillis; + int connectionTimeout = 30000; // 30 seconds + int progress = 0; + + wifiStateManager.setState(WiFiState_e::WiFiState_Connecting); + log_i("Trying to connect to: %s \n\r", ssid.c_str()); + + auto mdnsConfig = configManager.getMDNSConfig(); + WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, + INADDR_NONE); // need to call before setting hostname + WiFi.setHostname(mdnsConfig.hostname.c_str()); + WiFi.begin(ssid.c_str(), password.c_str(), channel); + while (WiFi.status() != WL_CONNECTED) { + progress++; + currentMillis = millis(); + Helpers::update_progress_bar(progress, 100); + delay(301); + if ((currentMillis - startingMillis) >= connectionTimeout) { + wifiStateManager.setState(WiFiState_e::WiFiState_Error); + log_e("Connection to: %s TIMEOUT \n\r", ssid.c_str()); + return false; + } + } + wifiStateManager.setState(WiFiState_e::WiFiState_Connected); + log_i("Successfully connected to %s \n\r", ssid.c_str()); + log_i("Setting TX power to: %d \n\r", (uint8_t)power); + WiFi.setTxPower(power); + return true; +} + +void WiFiHandler::update(ConfigState_e event) { + switch (event) { + case ConfigState_e::networksConfigUpdated: + this->begin(); + break; + default: + break; + } +} + +std::string WiFiHandler::getName() { + return "WiFiHandler"; +} diff --git a/ESP/lib/src/network/wifihandler/wifihandler.hpp b/ESP/lib/src/network/wifihandler/wifihandler.hpp new file mode 100644 index 0000000..11cdab0 --- /dev/null +++ b/ESP/lib/src/network/wifihandler/wifihandler.hpp @@ -0,0 +1,39 @@ +#pragma once +#ifndef WIFIHANDLER_HPP +#define WIFIHANDLER_HPP +#include +#include "data/config/project_config.hpp" +#include "data/utilities/Observer.hpp" + +class WiFiHandler : public IObserver { + public: + WiFiHandler(ProjectConfig& configManager, + const std::string& ssid, + const std::string& password, + uint8_t channel); + virtual ~WiFiHandler(); + void begin(); + void update(ConfigState_e event) override; + std::string getName() override; + + + bool _enable_adhoc; + + private: + void setUpADHOC(); + void adhoc(const std::string& ssid, + uint8_t channel, + const std::string& password = std::string()); + bool iniSTA(const std::string& ssid, + const std::string& password, + uint8_t channel, + wifi_power_t power); + + ProjectConfig& configManager; + + std::string ssid; + std::string password; + uint8_t channel; + uint8_t power; +}; +#endif // WIFIHANDLER_HPP diff --git a/ESP/src/main.cpp b/ESP/src/main.cpp index f4ace16..a94e1ce 100644 --- a/ESP/src/main.cpp +++ b/ESP/src/main.cpp @@ -1,15 +1,14 @@ #include #include #include -#include #include #include #include +#include #include #include -int STREAM_SERVER_PORT = 80; /** * @brief ProjectConfig object * @brief This is the main configuration object for the project @@ -18,30 +17,25 @@ int STREAM_SERVER_PORT = 80; */ ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME); -LEDManager ledManager(33, &ledStateManager); +LEDManager ledManager(33); #ifndef SIM_ENABLED -CameraHandler cameraHandler(&deviceConfig, &ledStateManager); +CameraHandler cameraHandler(deviceConfig); #endif // SIM_ENABLED -WiFiHandler wifiHandler(&deviceConfig, - &wifiStateManager, - &ledStateManager, - WIFI_SSID, - WIFI_PASSWORD, - WIFI_CHANNEL); +WiFiHandler wifiHandler(deviceConfig, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL); + +// ImprovHandler improvHandler(deviceConfig); #ifndef SIM_ENABLED -APIServer apiServer(&deviceConfig, - &cameraHandler, - &wifiStateManager, - "/control"); +APIServer apiServer(deviceConfig, cameraHandler, "/control"); #else -APIServer apiServer(&deviceConfig, NULL, &wifiStateManager, "/control"); +APIServer apiServer(deviceConfig, wifiStateManager, "/control"); #endif // SIM_ENABLED -MDNSHandler mdnsHandler(&mdnsStateManager, &deviceConfig); + +MDNSHandler mdnsHandler(deviceConfig); #ifndef SIM_ENABLED -StreamServer streamServer(STREAM_SERVER_PORT, &wifiStateManager); +StreamServer streamServer; #endif // SIM_ENABLED void setup() { @@ -51,13 +45,14 @@ void setup() { Serial.flush(); ledManager.begin(); #ifndef SIM_ENABLED - deviceConfig.attach(&cameraHandler); + deviceConfig.attach(cameraHandler); #endif // SIM_ENABLED - deviceConfig.attach(&mdnsHandler); + deviceConfig.attach(mdnsHandler); + deviceConfig.attach(wifiHandler); deviceConfig.initConfig(); deviceConfig.load(); wifiHandler._enable_adhoc = ENABLE_ADHOC; - wifiHandler.setupWifi(); + wifiHandler.begin(); mdnsHandler.startMDNS(); switch (wifiStateManager.getCurrentState()) { diff --git a/ESP/tools/customname.py b/ESP/tools/customname.py index cc6b836..7526fe7 100644 --- a/ESP/tools/customname.py +++ b/ESP/tools/customname.py @@ -144,6 +144,9 @@ def customName(project, version, commit, branch): "echo %s > %s" % (env["PROGNAME"], env.subst("./tools/firmware_name.txt")) ) + # replace the VERSION macro with the version from Git + env.Replace(VERSION="%s" % (s(defines.get("PIO_SRC_TAG")))) + try: flags = env["BUILD_FLAGS"] diff --git a/ESP/wokwi.toml b/ESP/wokwi.toml index bd5bebb..92a1754 100644 --- a/ESP/wokwi.toml +++ b/ESP/wokwi.toml @@ -1,7 +1,7 @@ [wokwi] version = 1 -elf = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-44d44d5-feautre-elegant-ota.elf" -firmware = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-44d44d5-feautre-elegant-ota.bin" +elf = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-c0391d0-feature-improv.elf" +firmware = ".pio/build/esp32AIThinker_sim/OpenIris-v1.10.6-esp32AIThinker_sim-c0391d0-feature-improv.bin" [[net.forward]] from = "localhost:8180" to = "target:80"