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();
}
}
}