using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using XiaoZhiSharp.Models; using XiaoZhiSharp.Protocols; using XiaoZhiSharp.Utils; namespace XiaoZhiSharp.Services { /// /// OTA服务类 /// public class OtaService { private readonly HttpClient _httpClient; private readonly string _userAgent; private readonly string _deviceId; private readonly string _clientId; private readonly string _acceptLanguage; public OtaService(string userAgent, string deviceId, string clientId, string acceptLanguage = "zh-CN") { _httpClient = new HttpClient(); _userAgent = userAgent; _deviceId = deviceId; _clientId = clientId; _acceptLanguage = acceptLanguage; // 设置HTTP客户端的超时时间 _httpClient.Timeout = TimeSpan.FromSeconds(30); } public async Task?> LoginAsync(string loginUrl, LoginModel request) { try { LogConsole.InfoLine($"开始用户登录,URL: {loginUrl}"); // 设置请求头 var httpRequest = new HttpRequestMessage(HttpMethod.Post, loginUrl); httpRequest.Headers.Add("Device-Id", _deviceId); httpRequest.Headers.Add("Client-Id", _clientId); httpRequest.Headers.Add("User-Agent", _userAgent); httpRequest.Headers.Add("Accept-Language", _acceptLanguage); // 序列化请求体 var jsonSetting = new JsonSerializerSettings(); jsonSetting.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(); var jsonContent = JsonConvert.SerializeObject(request, jsonSetting); LogConsole.InfoLine($"用户登录请求数据: {jsonContent}"); httpRequest.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); // 发送请求 var response = await _httpClient.SendAsync(httpRequest); var responseContent = await response.Content.ReadAsStringAsync(); LogConsole.InfoLine($"用户登录响应状态码: {response.StatusCode}"); LogConsole.InfoLine($"用户登录响应内容: {responseContent}"); if (response.IsSuccessStatusCode) { // 解析成功响应 var otaResponse = JsonConvert.DeserializeObject>(responseContent); LogConsole.InfoLine("用户登录成功"); return otaResponse; } else { // 解析错误响应 try { var errorResponse = JsonConvert.DeserializeObject(responseContent); LogConsole.ErrorLine($"用户登录失败: {errorResponse?.Error ?? "未知错误"}"); } catch { LogConsole.ErrorLine($"用户登录失败,HTTP状态码: {response.StatusCode}, 响应内容: {responseContent}"); } return null; } } catch (HttpRequestException httpEx) { LogConsole.ErrorLine($"用户登录网络请求异常: {httpEx.Message}"); return null; } catch (TaskCanceledException tcEx) { LogConsole.ErrorLine($"用户登录请求超时: {tcEx.Message}"); return null; } catch (Exception ex) { LogConsole.ErrorLine($"用户登录检查异常: {ex.Message}"); return null; } } /// /// 执行OTA检查 /// /// OTA服务器地址 /// OTA请求数据 /// OTA响应数据 public async Task CheckOtaAsync(string otaUrl, string token, OtaRequest request) { try { LogConsole.InfoLine($"开始OTA检查,URL: {otaUrl}"); // 设置请求头 var httpRequest = new HttpRequestMessage(HttpMethod.Post, otaUrl); httpRequest.Headers.Add("Device-Id", _deviceId); httpRequest.Headers.Add("Client-Id", _clientId); httpRequest.Headers.Add("User-Agent", _userAgent); httpRequest.Headers.Add("Accept-Language", _acceptLanguage); if (!string.IsNullOrWhiteSpace(token)) { httpRequest.Headers.Add("Authorization", token); } // 序列化请求体 var jsonContent = JsonConvert.SerializeObject(request, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); LogConsole.InfoLine($"OTA请求数据: {jsonContent}"); httpRequest.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); // 发送请求 var response = await _httpClient.SendAsync(httpRequest); var responseContent = await response.Content.ReadAsStringAsync(); LogConsole.InfoLine($"OTA响应状态码: {response.StatusCode}"); LogConsole.InfoLine($"OTA响应内容: {responseContent}"); if (response.IsSuccessStatusCode) { // 解析成功响应 var otaResponse = JsonConvert.DeserializeObject(responseContent); LogConsole.InfoLine("OTA检查成功"); return otaResponse; } else { // 解析错误响应 try { var errorResponse = JsonConvert.DeserializeObject(responseContent); LogConsole.ErrorLine($"OTA检查失败: {errorResponse?.Error ?? "未知错误"}"); } catch { LogConsole.ErrorLine($"OTA检查失败,HTTP状态码: {response.StatusCode}, 响应内容: {responseContent}"); } return null; } } catch (HttpRequestException httpEx) { LogConsole.ErrorLine($"OTA网络请求异常: {httpEx.Message}"); return null; } catch (TaskCanceledException tcEx) { LogConsole.ErrorLine($"OTA请求超时: {tcEx.Message}"); return null; } catch (Exception ex) { LogConsole.ErrorLine($"OTA检查异常: {ex.Message}"); return null; } } /// /// 创建默认的OTA请求对象 /// /// 当前应用版本 /// ELF文件SHA256哈希值 /// 开发板类型 /// 开发板名称 /// OTA请求对象 public OtaRequest CreateDefaultOtaRequest(string version = "1.0.0", string elfSha256 = "", string boardType = "xiaozhi-sharp", string boardName = "xiaozhi-sharp-client") { var request = new OtaRequest { Application = new ApplicationInfo { Name = "xiaozhi", Version = version, ElfSha256 = !string.IsNullOrEmpty(elfSha256) ? elfSha256 : GenerateDefaultSha256(), CompileTime = DateTime.UtcNow.ToString("MMM dd yyyy HH:mm:ss") + "Z", IdfVersion = "net8.0" }, MacAddress = _deviceId, Uuid = _clientId, PartitionTable = new System.Collections.Generic.List() { new PartitionInfo() { Label = "ota_0", Type = 1, Subtype = 1, Address = 0x10000, Size = 0x140000 }, }, Board = new BoardInfo { Type = boardType, Name = boardName, Mac = _deviceId }, Version = 2, Language = _acceptLanguage, Ota = new OtaInfo { Label = "normal" } }; return request; } /// /// 创建包含网络信息的OTA请求对象 /// /// 当前应用版本 /// ELF文件SHA256哈希值 /// 开发板类型 /// 开发板名称 /// WiFi网络名称 /// WiFi信号强度 /// WiFi频道 /// 设备IP地址 /// OTA请求对象 public OtaRequest CreateWifiOtaRequest(string version = "1.0.0", string elfSha256 = "", string boardType = "xiaozhi-sharp-wifi", string boardName = "xiaozhi-sharp-wifi-client", string ssid = "", int rssi = -50, int channel = 1, string ip = "") { var request = CreateDefaultOtaRequest(version, elfSha256, boardType, boardName); // 添加WiFi信息 request.Board.Ssid = ssid; request.Board.Rssi = rssi; request.Board.Channel = channel; request.Board.Ip = ip; return request; } /// /// 生成默认的SHA256哈希值(示例值) /// /// SHA256哈希字符串 private string GenerateDefaultSha256() { // 这里生成一个示例SHA256值,实际使用时应该是真实的文件哈希 return "c8a8ecb6d6fbcda682494d9675cd1ead240ecf38bdde75282a42365a0e396033"; } /// /// 释放资源 /// public void Dispose() { _httpClient?.Dispose(); } } }