280 lines
11 KiB
C#
280 lines
11 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// OTA服务类
|
||
/// </summary>
|
||
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<TResult<object>?> 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<TResult<object>>(responseContent);
|
||
LogConsole.InfoLine("用户登录成功");
|
||
return otaResponse;
|
||
}
|
||
else
|
||
{
|
||
// 解析错误响应
|
||
try
|
||
{
|
||
var errorResponse = JsonConvert.DeserializeObject<OtaErrorResponse>(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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行OTA检查
|
||
/// </summary>
|
||
/// <param name="otaUrl">OTA服务器地址</param>
|
||
/// <param name="request">OTA请求数据</param>
|
||
/// <returns>OTA响应数据</returns>
|
||
public async Task<OtaResponse?> 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<OtaResponse>(responseContent);
|
||
LogConsole.InfoLine("OTA检查成功");
|
||
return otaResponse;
|
||
}
|
||
else
|
||
{
|
||
// 解析错误响应
|
||
try
|
||
{
|
||
var errorResponse = JsonConvert.DeserializeObject<OtaErrorResponse>(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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建默认的OTA请求对象
|
||
/// </summary>
|
||
/// <param name="version">当前应用版本</param>
|
||
/// <param name="elfSha256">ELF文件SHA256哈希值</param>
|
||
/// <param name="boardType">开发板类型</param>
|
||
/// <param name="boardName">开发板名称</param>
|
||
/// <returns>OTA请求对象</returns>
|
||
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<PartitionInfo>()
|
||
{
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建包含网络信息的OTA请求对象
|
||
/// </summary>
|
||
/// <param name="version">当前应用版本</param>
|
||
/// <param name="elfSha256">ELF文件SHA256哈希值</param>
|
||
/// <param name="boardType">开发板类型</param>
|
||
/// <param name="boardName">开发板名称</param>
|
||
/// <param name="ssid">WiFi网络名称</param>
|
||
/// <param name="rssi">WiFi信号强度</param>
|
||
/// <param name="channel">WiFi频道</param>
|
||
/// <param name="ip">设备IP地址</param>
|
||
/// <returns>OTA请求对象</returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成默认的SHA256哈希值(示例值)
|
||
/// </summary>
|
||
/// <returns>SHA256哈希字符串</returns>
|
||
private string GenerateDefaultSha256()
|
||
{
|
||
// 这里生成一个示例SHA256值,实际使用时应该是真实的文件哈希
|
||
return "c8a8ecb6d6fbcda682494d9675cd1ead240ecf38bdde75282a42365a0e396033";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
_httpClient?.Dispose();
|
||
}
|
||
}
|
||
}
|