#include #include #include #include #include #include "cmd_system.h" #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include "esp_log.h" #include "esp_wifi_types.h" #include "http_server_handlers.h" #include "messaging.h" #include "network_ethernet.h" #include "network_status.h" #include "network_wifi.h" #include "state_machine.h" #include "trace.h" #include "dns_server.h" BaseType_t network_manager_task; /* objects used to manipulate the main queue of events */ QueueHandle_t network_manager_queue; using namespace stateless; using namespace std::placeholders; static const char TAG[] = "network_manager"; static TaskHandle_t task_network_manager = NULL; TimerHandle_t STA_timer = NULL; uint32_t STA_duration; static int32_t total_connected_time = 0; static int64_t last_connected = 0; static uint16_t num_disconnect = 0; void network_wifi_get_stats(int32_t* ret_total_connected_time, int64_t* ret_last_connected, uint16_t* ret_num_disconnect) { *ret_total_connected_time = total_connected_time; *ret_last_connected = last_connected; *ret_num_disconnect = num_disconnect; } namespace { std::ostream& operator<<(std::ostream& os, const state_t s) { //static const char* name[] = { "idle", "stopped", "started", "running" }; static const char* name[] = { "initializing", "global", "eth_starting", "eth_active", "eth_active_linkup", "eth_active_connected", "eth_active_linkdown", "wifi_up", "wifi_initializing", "network_manager_async", "wifi_connecting_scanning", "wifi_connecting", "wifi_connected", "wifi_disconnecting", "wifi_user_disconnected", "wifi_connected_waiting_for_ip", "wifi_connected_scanning", "wifi_lost_connection", "wifi_ap_mode", "wifi_ap_mode_scanning", "wifi_ap_mode_scan_done", "wifi_ap_mode_connecting", "wifi_ap_mode_connected", "system_rebooting"}; os << name[(int)s]; return os; } const char* trigger_to_string(trig_t trigger) { switch (trigger) { ENUM_TO_STRING(t_link_up); ENUM_TO_STRING(t_link_down); ENUM_TO_STRING(t_configure); ENUM_TO_STRING(t_got_ip); ENUM_TO_STRING(t_next); ENUM_TO_STRING(t_start); ENUM_TO_STRING(t_scan); ENUM_TO_STRING(t_fail); ENUM_TO_STRING(t_success); ENUM_TO_STRING(t_scan_done); ENUM_TO_STRING(t_connect); ENUM_TO_STRING(t_reboot); ENUM_TO_STRING(t_reboot_url); ENUM_TO_STRING(t_lost_connection); ENUM_TO_STRING(t_update_status); ENUM_TO_STRING(t_disconnect); default: break; } return "Unknown trigger"; } std::ostream& operator<<(std::ostream& os, const trig_t & t) { //static const char* name[] = { "start", "stop", "set_speed", "halt" }; os << trigger_to_string(t); return os; } } // namespace // namespace stateless // { // template<> void print_state(std::ostream& os, const state& s) // { os << s; } // template<> void print_trigger 0) total_connected_time += ((esp_timer_get_time() - last_connected) / (1000 * 1000)); last_connected = 0; num_disconnect++; ESP_LOGW(TAG, "Wifi disconnected. Number of disconnects: %d, Average time connected: %d", num_disconnect, num_disconnect > 0 ? (total_connected_time / num_disconnect) : 0); //network_manager_async_connect(wifi_manager_get_wifi_sta_config()); if (retries < WIFI_MANAGER_MAX_RETRY) { ESP_LOGD(TAG, "Issuing ORDER_CONNECT_STA to retry connection."); retries++; network_manager_async_connect(wifi_manager_get_wifi_sta_config()); free(disconnected_event); } else { /* In this scenario the connection was lost beyond repair: kick start the AP! */ retries = 0; wifi_mode_t mode; ESP_LOGW(TAG, "All connect retry attempts failed."); /* put us in softAP mode first */ esp_wifi_get_mode(&mode); if (WIFI_MODE_APSTA != mode) { STA_duration = STA_POLLING_MIN; network_manager_async_lost_connection(disconnected_event); } else if (STA_duration < STA_POLLING_MAX) { STA_duration *= 1.25; free(disconnected_event); } /* keep polling for existing connection */ xTimerChangePeriod(STA_timer, pdMS_TO_TICKS(STA_duration), portMAX_DELAY); xTimerStart(STA_timer, portMAX_DELAY); ESP_LOGD(TAG, "STA search slow polling of %d", STA_duration); } }) .on_entry([=](const TTransition& t) { wifi_manager_safe_reset_sta_ip_string(); }) .permit(trig_t::t_connect, state_t::wifi_connecting) .permit(trig_t::t_lost_connection, state_t::wifi_ap_mode); // Register a callback for state transitions (the default does nothing). sm_.on_transition([](const TTransition& t) { std::cout << "transition from [" << t.source() << "] to [" << t.destination() << "] via trigger [" << t.trigger() << "]" << std::endl; }); // Override the default behaviour of throwing when a trigger is unhandled. sm_.on_unhandled_trigger([](const state_t s, const trig_t t) { std::cerr << "ignore unhandled trigger [" << t << "] in state [" << s << "]" << std::endl; }); } public: bool Allowed(trig_t trigger) { if (!sm_.can_fire(trigger)) { ESP_LOGW(TAG, "Network manager might not be able to process trigger %s", trigger_to_string(trigger)); return false; } return true; } bool Fire(trig_t trigger) { try { sm_.fire(trigger); return true; } catch (const std::exception& e) { std::cerr << e.what() << '\n'; } return false; } bool Event(queue_message& msg) { trig_t trigger = msg.trigger; try { if (trigger == trig_t::t_connect) { sm_.fire(connect_trigger_, msg.wifi_config); } else if (trigger == trig_t::t_reboot) { sm_.fire(reboot_trigger_, msg.rtype); } else if (trigger == trig_t::t_reboot_url) { sm_.fire(reboot_ota_, msg.strval); } else if (trigger == trig_t::t_lost_connection) { sm_.fire(disconnected_, msg.disconnected_event); } else { sm_.fire(trigger); } return true; } catch (const std::exception& e) { std::cerr << e.what() << '\n'; return false; } } private: uint8_t reconnect_attempt = 0; bool existing_connection = false; uint8_t retries = 0; TStateMachine sm_; TConnectTrigger connect_trigger_; TRebootTrigger reboot_trigger_; TRebootOTATrigger reboot_ota_; TDisconnectTrigger disconnected_; }; NetworkManager nm; void network_manager_start() { nm.Fire(trig_t::t_start); } bool network_manager_async(trig_t trigger) { queue_message msg; msg.trigger = trigger; if (nm.Allowed(trigger)) { return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); } return false; } bool network_manager_async_fail() { return network_manager_async(trig_t::t_fail); } bool network_manager_async_success() { return network_manager_async(trig_t::t_success); } bool network_manager_async_link_up() { return network_manager_async(trig_t::t_link_up); } bool network_manager_async_link_down() { return network_manager_async(trig_t::t_link_down); } bool network_manager_async_configure() { return network_manager_async(trig_t::t_configure); } bool network_manager_async_got_ip() { return network_manager_async(trig_t::t_got_ip); } bool network_manager_async_next() { return network_manager_async(trig_t::t_next); } bool network_manager_async_start() { return network_manager_async(trig_t::t_start); } bool network_manager_async_scan() { return network_manager_async(trig_t::t_scan); } bool network_manager_async_update_status() { return network_manager_async(trig_t::t_update_status); } bool network_manager_async_disconnect() { return network_manager_async(trig_t::t_disconnect); } bool network_manager_async_scan_done() { return network_manager_async(trig_t::t_scan_done); } bool network_manager_async_connect(wifi_config_t* wifi_config) { queue_message msg; msg.trigger = trig_t::t_connect; msg.wifi_config = wifi_config; if (nm.Allowed(msg.trigger)) { return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); } return false; } bool network_manager_async_lost_connection(wifi_event_sta_disconnected_t* disconnected_event) { queue_message msg; msg.trigger = trig_t::t_lost_connection; msg.disconnected_event = disconnected_event; if (nm.Allowed(msg.trigger)) { return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); } return false; } bool network_manager_async_reboot(reboot_type_t rtype) { queue_message msg; msg.trigger = trig_t::t_reboot; msg.rtype = rtype; if (nm.Allowed(msg.trigger)) { return xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); } return false; } void network_manager_reboot_ota(char* url) { queue_message msg; if (url == NULL) { msg.trigger = trig_t::t_reboot; msg.rtype = reboot_type_t::OTA; } else { msg.trigger = trig_t::t_reboot_url; msg.strval = strdup(url); } if (nm.Allowed(msg.trigger)) { xQueueSendToFront(network_manager_queue, &msg, portMAX_DELAY); } return; } // switch (msg.code) { // case EVENT_SCAN_DONE: // err = wifi_scan_done(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) { // ESP_LOGD(TAG, "Invoking SCAN DONE callback"); // (*cb_ptr_arr[msg.code])(NULL); // ESP_LOGD(TAG, "Done Invoking SCAN DONE callback"); // } // break; // case ORDER_START_WIFI_SCAN: // err = network_wifi_start_scan(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case ORDER_LOAD_AND_RESTORE_STA: // err = network_wifi_load_restore(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case ORDER_CONNECT_STA: // err = network_wifi_order_connect(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case EVENT_STA_DISCONNECTED: // err = network_wifi_disconnected(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case ORDER_START_AP: // err = network_wifi_start_ap(&msg); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case EVENT_GOT_IP: // ESP_LOGD(TAG, "MESSAGE: EVENT_GOT_IP"); // /* save IP as a string for the HTTP server host */ // //s->ip_info.ip.addr // ip_event_got_ip_t* event = (ip_event_got_ip_t*)msg.param; // wifi_manager_safe_update_sta_ip_string(&(event->ip_info.ip)); // wifi_manager_generate_ip_info_json(network_manager_is_flag_set(WIFI_MANAGER_REQUEST_STA_CONNECT_FAILED_BIT)? UPDATE_FAILED_ATTEMPT_AND_RESTORE : UPDATE_CONNECTION_OK, event->esp_netif, event->ip_changed); // free(msg.param); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case UPDATE_CONNECTION_OK: // messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "UPDATE_CONNECTION_OK not implemented"); // break; // case ORDER_DISCONNECT_STA: // ESP_LOGD(TAG, "MESSAGE: ORDER_DISCONNECT_STA. Calling esp_wifi_disconnect()"); // /* precise this is coming from a user request */ // xEventGroupSetBits(wifi_manager_event_group, WIFI_MANAGER_REQUEST_DISCONNECT_BIT); // /* order wifi discconect */ // ESP_ERROR_CHECK(esp_wifi_disconnect()); // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case ORDER_RESTART_OTA_URL: // ESP_LOGD(TAG, "Calling start_ota."); // start_ota(msg.param, NULL, 0); // free(msg.param); // break; // case ORDER_RESTART_RECOVERY: // break; // case ORDER_RESTART: // ESP_LOGD(TAG, "Calling simple_restart."); // simple_restart(); // break; // case ORDER_UPDATE_STATUS: // ; // break; // case EVENT_ETH_TIMEOUT: // ESP_LOGW(TAG, "Ethernet connection timeout. Rebooting with WiFi Active"); // network_manager_reboot(RESTART); // break; // case EVENT_ETH_LINK_UP: // /* callback */ // ESP_LOGD(TAG,"EVENT_ETH_LINK_UP message received"); // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // case EVENT_ETH_LINK_DOWN: // /* callback */ // if (cb_ptr_arr[msg.code]) // (*cb_ptr_arr[msg.code])(NULL); // break; // default: // break; // } /* end of switch/case */ // if (!network_ethernet_wait_for_link(500)) { // if(network_ethernet_enabled()){ // ESP_LOGW(TAG, "Ethernet not connected. Starting Wifi"); // } // init_network_wifi(); // wifi_manager_send_message(ORDER_LOAD_AND_RESTORE_STA, NULL); // } void network_manager(void* pvParameters) { queue_message msg; esp_err_t err = ESP_OK; BaseType_t xStatus; network_manager_async(trig_t::t_start); /* main processing loop */ for (;;) { xStatus = xQueueReceive(network_manager_queue, &msg, portMAX_DELAY); if (xStatus == pdPASS) { // pass the event to the sync processor nm.Event(msg); } /* end of if status=pdPASS */ } /* end of for loop */ vTaskDelete(NULL); } void network_manager_destroy() { vTaskDelete(task_network_manager); task_network_manager = NULL; /* heap buffers */ destroy_network_status(); destroy_network_wifi(); destroy_network_status(); /* RTOS objects */ vQueueDelete(network_manager_queue); network_manager_queue = NULL; }