forked from xiaozhi/xiaozhi-esp32
v2.1.0: Upgrade esp-wifi-connect to 3.0; New device state machine (#1528)
* Upgrade component version * update fonts component version * Handle OTA error code * Update project version to 2.1.0 and add device state machine implementation - Upgrade esp-wifi-connect to 3.0.0, allowing reconfiguring wifi without rebooting - Introduce device state machine with state change notification in new files - Remove obsolete device state event files - Update application logic to utilize new state machine - Minor adjustments in various board implementations for state handling * fix compile errors * Refactor power saving mode implementation to use PowerSaveLevel enumeration - Updated Application class to replace SetPowerSaveMode with SetPowerSaveLevel, allowing for LOW_POWER and PERFORMANCE settings. - Modified various board implementations to align with the new power save level structure. - Ensured consistent handling of power save levels across different board files, enhancing code maintainability and clarity. * Refactor power save level checks across multiple board implementations - Updated the condition for power save level checks in various board files to ensure that the power save timer only wakes up when the level is not set to LOW_POWER. - Improved consistency in handling power save levels, enhancing code clarity and maintainability. * Refactor EnterWifiConfigMode calls in board implementations - Updated calls to EnterWifiConfigMode to use the appropriate instance reference (self or board) across multiple board files. - Improved code consistency and clarity in handling device state during WiFi configuration mode entry. * Add cellular modem event handling and improve network status updates - Introduced new network events for cellular modem operations, including detecting, registration errors, and timeouts. - Enhanced the Application class to handle different network states and update the display status accordingly. - Refactored Ml307Board to implement a callback mechanism for network events, improving modularity and responsiveness. - Updated dual_network_board and board headers to support new network event callbacks, ensuring consistent handling across board implementations. * update esp-wifi-connect version * Update WiFi configuration tool messages across multiple board implementations to clarify user actions
This commit is contained in:
161
main/device_state_machine.cc
Normal file
161
main/device_state_machine.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "device_state_machine.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char* TAG = "StateMachine";
|
||||
|
||||
// State name strings for logging
|
||||
static const char* const STATE_STRINGS[] = {
|
||||
"unknown",
|
||||
"starting",
|
||||
"wifi_configuring",
|
||||
"idle",
|
||||
"connecting",
|
||||
"listening",
|
||||
"speaking",
|
||||
"upgrading",
|
||||
"activating",
|
||||
"audio_testing",
|
||||
"fatal_error",
|
||||
"invalid_state"
|
||||
};
|
||||
|
||||
DeviceStateMachine::DeviceStateMachine() {
|
||||
}
|
||||
|
||||
const char* DeviceStateMachine::GetStateName(DeviceState state) {
|
||||
if (state >= 0 && state <= kDeviceStateFatalError) {
|
||||
return STATE_STRINGS[state];
|
||||
}
|
||||
return STATE_STRINGS[kDeviceStateFatalError + 1];
|
||||
}
|
||||
|
||||
bool DeviceStateMachine::IsValidTransition(DeviceState from, DeviceState to) const {
|
||||
// Allow transition to the same state (no-op)
|
||||
if (from == to) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Define valid state transitions based on the state diagram
|
||||
switch (from) {
|
||||
case kDeviceStateUnknown:
|
||||
// Can only go to starting
|
||||
return to == kDeviceStateStarting;
|
||||
|
||||
case kDeviceStateStarting:
|
||||
// Can go to wifi configuring or activating
|
||||
return to == kDeviceStateWifiConfiguring ||
|
||||
to == kDeviceStateActivating;
|
||||
|
||||
case kDeviceStateWifiConfiguring:
|
||||
// Can go to activating (after wifi connected) or audio testing
|
||||
return to == kDeviceStateActivating ||
|
||||
to == kDeviceStateAudioTesting;
|
||||
|
||||
case kDeviceStateAudioTesting:
|
||||
// Can go back to wifi configuring
|
||||
return to == kDeviceStateWifiConfiguring;
|
||||
|
||||
case kDeviceStateActivating:
|
||||
// Can go to upgrading, idle, or back to wifi configuring (on error)
|
||||
return to == kDeviceStateUpgrading ||
|
||||
to == kDeviceStateIdle ||
|
||||
to == kDeviceStateWifiConfiguring;
|
||||
|
||||
case kDeviceStateUpgrading:
|
||||
// Can go to idle (upgrade failed) or activating
|
||||
return to == kDeviceStateIdle ||
|
||||
to == kDeviceStateActivating;
|
||||
|
||||
case kDeviceStateIdle:
|
||||
// Can go to connecting, listening (manual mode), speaking, activating, upgrading, or wifi configuring
|
||||
return to == kDeviceStateConnecting ||
|
||||
to == kDeviceStateListening ||
|
||||
to == kDeviceStateSpeaking ||
|
||||
to == kDeviceStateActivating ||
|
||||
to == kDeviceStateUpgrading ||
|
||||
to == kDeviceStateWifiConfiguring;
|
||||
|
||||
case kDeviceStateConnecting:
|
||||
// Can go to idle (failed) or listening (success)
|
||||
return to == kDeviceStateIdle ||
|
||||
to == kDeviceStateListening;
|
||||
|
||||
case kDeviceStateListening:
|
||||
// Can go to speaking or idle
|
||||
return to == kDeviceStateSpeaking ||
|
||||
to == kDeviceStateIdle;
|
||||
|
||||
case kDeviceStateSpeaking:
|
||||
// Can go to listening or idle
|
||||
return to == kDeviceStateListening ||
|
||||
to == kDeviceStateIdle;
|
||||
|
||||
case kDeviceStateFatalError:
|
||||
// Cannot transition out of fatal error
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceStateMachine::CanTransitionTo(DeviceState target) const {
|
||||
return IsValidTransition(current_state_.load(), target);
|
||||
}
|
||||
|
||||
bool DeviceStateMachine::TransitionTo(DeviceState new_state) {
|
||||
DeviceState old_state = current_state_.load();
|
||||
|
||||
// No-op if already in the target state
|
||||
if (old_state == new_state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate transition
|
||||
if (!IsValidTransition(old_state, new_state)) {
|
||||
ESP_LOGW(TAG, "Invalid state transition: %s -> %s",
|
||||
GetStateName(old_state), GetStateName(new_state));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform transition
|
||||
current_state_.store(new_state);
|
||||
ESP_LOGI(TAG, "State: %s -> %s",
|
||||
GetStateName(old_state), GetStateName(new_state));
|
||||
|
||||
// Notify callback
|
||||
NotifyStateChange(old_state, new_state);
|
||||
return true;
|
||||
}
|
||||
|
||||
int DeviceStateMachine::AddStateChangeListener(StateCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
int id = next_listener_id_++;
|
||||
listeners_.emplace_back(id, std::move(callback));
|
||||
return id;
|
||||
}
|
||||
|
||||
void DeviceStateMachine::RemoveStateChangeListener(int listener_id) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
listeners_.erase(
|
||||
std::remove_if(listeners_.begin(), listeners_.end(),
|
||||
[listener_id](const auto& p) { return p.first == listener_id; }),
|
||||
listeners_.end());
|
||||
}
|
||||
|
||||
void DeviceStateMachine::NotifyStateChange(DeviceState old_state, DeviceState new_state) {
|
||||
std::vector<StateCallback> callbacks_copy;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
callbacks_copy.reserve(listeners_.size());
|
||||
for (const auto& [id, cb] : listeners_) {
|
||||
callbacks_copy.push_back(cb);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& cb : callbacks_copy) {
|
||||
cb(old_state, new_state);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user