Files
xiaozhi-esp32/main/device_state_machine.cc

162 lines
5.0 KiB
C++
Raw Normal View History

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
2025-12-09 09:24:56 +08:00
#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);
}
}