Managers/APConfigManager.cs

Managers/APConfigManager.cs

using Microsoft.Extensions.Logging;
using nanoFramework.Logging;
using nanoFramework.WebServer;
using System;
using System.Net;
using System.Threading;

namespace YeFanIoTTest.Managers
{
// AP配网状态枚举
public enum APConfigState
{
Idle, // 空闲
APConfiguring, // 正在配网
Connecting, // 正在连接
Configured, // 配网成功
Failed // 配网失败
}

// 配网完成事件参数
public class APConfigResultEventArgs : EventArgs
{
public bool Success { get; set; }
public string SSID { get; set; }
public string Message { get; set; }
}

internal class APConfigManager
{
// 日志记录器
private ILogger _logger;

// WIFI管理器(外部注入,不内部创建)
private WifiManager _wifiManager;

// Web服务器实例
private WebServer _webServer;

// 当前配网状态
public APConfigState CurrentState { get; private set; } = APConfigState.Idle;

// 配网完成事件(通知外部配网结果)
public event APConfigResultEventHandler OnConfigCompleted;
public delegate void APConfigResultEventHandler(object sender, APConfigResultEventArgs e);

// AP配网参数
private const string AP_SSID = "YF3300_ESP32S3"; // 热点名称
private const string AP_PASSWORD = "yf123456"; // 热点密码(WPA2需>=8)
private const string AP_IP = "192.168.4.1"; // AP网关IP

// Web服务器参数
private const int WEB_PORT = 80; // Web服务器端口
private const int CONFIG_TIMEOUT = 600000; // 配网超时时间(10分钟)

// 配网任务取消令牌
private CancellationTokenSource _configCts;

// 配网超时定时器
private Timer _timeoutTimer;

// 当前配网的SSID和密码(用于传递给WebServerController)
public static string PendingSSID { get; set; }
public static string PendingPassword { get; set; }
public static bool ConfigSubmitted { get; set; } = false;

// WifiManager实例(供WebServerController访问)
private static WifiManager _staticWifiManager;

// 获取WifiManager实例
public static WifiManager GetWifiManager()
{
return _staticWifiManager;
}

// 触发配网完成事件(由外部调用)
public void RaiseConfigCompleted(bool success, string ssid, string message)
{
if (OnConfigCompleted != null)
{
OnConfigCompleted(this, new APConfigResultEventArgs()
{
Success = success,
SSID = ssid,
Message = message
});
}
}

// 构造函数:注入WIFI管理器实例
public APConfigManager(WifiManager wifiManager)
{
if (wifiManager == null)
{
throw new ArgumentNullException(nameof(wifiManager));
}
_wifiManager = wifiManager;
_staticWifiManager = wifiManager; // 保存到静态字段
_logger = LogDispatcher.LoggerFactory.CreateLogger("APConfigManager");
}

#region 公共API

// 检查是否已有保存的WIFI配置
public bool HasSavedWifiConfig()
{
return _wifiManager.HasSavedConfig();
}

// 尝试使用已保存的配置连接WIFI
public bool TryConnectSavedWifiConfig()
{
if (_wifiManager.LoadCredentials(out string ssid, out string password))
{
_logger.LogInformation($"[APConfigManager] 尝试使用已保存的WIFI配置进行连接: {ssid}");
return _wifiManager.ConnectSTA(ssid, password, enableReconnect: true);
}
return false;
}

// 开始配网
public bool StartAPConfig()
{
if (CurrentState != APConfigState.Idle)
{
_logger.LogWarning("[APConfigManager] 当前AP状态不是空闲状态,无法开始配网");
return false;
}

_logger.LogInformation("[APConfigManager] 开始配网");

// 步骤一:启动AP热点
bool apStarted = _wifiManager.StartAP(AP_SSID, AP_PASSWORD, AP_IP);
if (!apStarted)
{
_logger.LogError("[APConfigManager] 启动AP热点失败");
CurrentState = APConfigState.Failed;
return false;
}

CurrentState = APConfigState.APConfiguring;
_logger.LogInformation($"[APConfigManager] AP热点已启动,热点名称:{AP_SSID}, IP:{AP_IP}");

// 步骤二:启动Web服务器
// 【重要】等待网络接口完全初始化后再启动Web服务器
Thread.Sleep(1000);
try
{
// 重置配网提交标志
ConfigSubmitted = false;

// 创建Web服务器实例
// 【重要】在AP模式下,必须显式指定绑定的IP地址,否则会绑定到错误的网络接口
_webServer = new WebServer(WEB_PORT, HttpProtocol.Http, IPAddress.Parse(AP_IP), new Type[] { typeof(WebServerController) });
_webServer.Start();
_logger.LogInformation($"[APConfigManager] Web服务器已启动,端口:{WEB_PORT},绑定IP:{AP_IP}");

// ========== 诊断信息:验证网络接口状态 ==========
try
{
var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
foreach (var ni in interfaces)
{
if (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.WirelessAP)
{
_logger.LogInformation($"[APConfigManager] AP接口状态: {ni.IPv4Address}");
_logger.LogInformation($"[APConfigManager] AP接口类型: {ni.NetworkInterfaceType}");
}
}
}
catch (Exception ex)
{
_logger.LogWarning($"[APConfigManager] 获取网络接口信息失败: {ex.Message}");
}
}
catch (Exception e)
{
_logger.LogError($"[APConfigManager] 启动Web服务器失败: {e.Message}");
StopAPConfig();
return false;
}

// 步骤三:启动配网超时定时器
_configCts = new CancellationTokenSource();
_timeoutTimer = new Timer(OnTimeoutCallback, null, CONFIG_TIMEOUT, Timeout.Infinite);

_logger.LogInformation($"[APConfigManager] 请连接WIFI {AP_SSID},并访问 http://{AP_IP}");
_logger.LogInformation($"[APConfigManager] 配网超时定时器已启动,超时时间:{CONFIG_TIMEOUT / 1000} 秒");

return true;
}

// 停止AP配网(清理资源)
public void StopAPConfig()
{
_logger.LogInformation("[APConfigManager] 停止AP配网");

// 停止超时定时器
if (_timeoutTimer != null)
{
_timeoutTimer.Dispose();
_timeoutTimer = null;
}

// 取消配网任务
if (_configCts != null)
{
_configCts.Cancel();
_configCts.Dispose();
_configCts = null;
}

// 关闭Web服务器
if (_webServer != null)
{
_webServer.Stop();
_webServer.Dispose();
_webServer = null;
}

// 关闭AP热点
_wifiManager.StopAP();

CurrentState = APConfigState.Idle;
_logger.LogInformation("[APConfigManager] AP配网已停止");
}

// 处理配网提交(由WebServerController调用)
public void HandleConfigSubmit(string ssid, string password)
{
_logger.LogInformation($"[APConfigManager] 收到配网请求:{ssid}");

// 停止超时定时器
if (_timeoutTimer != null)
{
_timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite);
}

// 停止Web服务器
if (_webServer != null)
{
_webServer.Stop();
}

CurrentState = APConfigState.Connecting;

// 尝试连接WIFI
bool connected = _wifiManager.ConnectSTA(ssid, password, enableReconnect: true);

if (connected)
{
// ========== 验证网络可达性 ==========
// 检查WiFi是否真的可以上网(通过DNS解析验证)
_logger.LogInformation("[APConfigManager] 正在验证网络可达性...");
bool networkReachable = _wifiManager.IsNetworkReachable();

if (!networkReachable)
{
_logger.LogWarning("[APConfigManager] WiFi已连接但网络不可达,请检查路由器是否连接外网");
// 仍然保存凭证,但提示用户网络可能不可用
}
else
{
_logger.LogInformation("[APConfigManager] 网络可达性验证成功,可以正常上网");
}

// ========== 保存WIFI凭证到Flash ==========
// 【测试时可注释以下2行,关闭持久化存储功能】
// _wifiManager.SaveCredentials(ssid, password);
_logger.LogInformation($"[APConfigManager] 已保存WIFI凭证:{ssid}");

CurrentState = APConfigState.Configured;
_logger.LogInformation($"[APConfigManager] 配网成功!SSID:{ssid}");

// 触发配网完成事件
if (OnConfigCompleted != null)
{
OnConfigCompleted(this, new APConfigResultEventArgs()
{
Success = true,
SSID = ssid,
Message = networkReachable ? "配网成功!网络可达" : "配网成功!但网络不可达,请检查路由器"
});
}
}
else
{
CurrentState = APConfigState.Failed;
_logger.LogError($"[APConfigManager] 配网失败!SSID:{ssid}");

// 触发配网失败事件
if (OnConfigCompleted != null)
{
OnConfigCompleted(this, new APConfigResultEventArgs()
{
Success = false,
SSID = ssid,
Message = "配网失败!"
});
}

// 重新启动AP配网
Thread.Sleep(2000);
CurrentState = APConfigState.Idle;
StartAPConfig();
}
}

#endregion

#region 私有方法

// 超时回调
private void OnTimeoutCallback(object state)
{
_logger.LogWarning("[APConfigManager] 配网超时!");

// 触发配网超时事件
if (OnConfigCompleted != null)
{
OnConfigCompleted(this, new APConfigResultEventArgs()
{
Success = false,
SSID = null,
Message = "配网超时!"
});
}

// 停止AP配网
StopAPConfig();
}

#endregion
}
}