增加摄像头中控台项目
This commit is contained in:
119
SHH.CameraDashboard/Services/HttpService.cs
Normal file
119
SHH.CameraDashboard/Services/HttpService.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace SHH.CameraDashboard
|
||||
{
|
||||
public static class HttpService
|
||||
{
|
||||
// 单例 HttpClient,避免端口耗尽
|
||||
private static readonly HttpClient _client;
|
||||
|
||||
// 【关键】日志事件,UI层订阅这个事件来显示日志
|
||||
public static event Action<ApiLogEntry> OnApiLog;
|
||||
|
||||
static HttpService()
|
||||
{
|
||||
_client = new HttpClient();
|
||||
// 设置一个合理的超时,避免界面卡死
|
||||
_client.Timeout = TimeSpan.FromSeconds(5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 泛型 GET 方法
|
||||
/// </summary>
|
||||
public static async Task<T> GetAsync<T>(string url)
|
||||
{
|
||||
return await ExecuteRequestAsync<T>(new HttpRequestMessage(HttpMethod.Get, url));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 泛型 POST 方法
|
||||
/// </summary>
|
||||
public static async Task<T> PostAsync<T>(string url, object data)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
string json = JsonConvert.SerializeObject(data);
|
||||
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
return await ExecuteRequestAsync<T>(request, json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 专门用于【连通性测试】的方法 (不关心返回值内容,只关心通不通)
|
||||
/// </summary>
|
||||
public static async Task<bool> TestConnectionAsync(string url)
|
||||
{
|
||||
// 复用核心逻辑,但泛型传 string (忽略结果) 或 object
|
||||
try
|
||||
{
|
||||
await ExecuteRequestAsync<string>(new HttpRequestMessage(HttpMethod.Get, url));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 核心执行逻辑 ---
|
||||
private static async Task<T> ExecuteRequestAsync<T>(HttpRequestMessage request, string requestBody = "")
|
||||
{
|
||||
// 1. 准备日志对象
|
||||
var log = new ApiLogEntry
|
||||
{
|
||||
Method = request.Method.ToString(),
|
||||
Url = request.RequestUri.ToString(),
|
||||
RequestBody = requestBody
|
||||
};
|
||||
|
||||
var sw = Stopwatch.StartNew(); // 开始计时
|
||||
|
||||
try
|
||||
{
|
||||
// 2. 发起网络请求
|
||||
var response = await _client.SendAsync(request);
|
||||
|
||||
sw.Stop(); // 停止计时
|
||||
log.DurationMs = sw.ElapsedMilliseconds;
|
||||
log.StatusCode = (int)response.StatusCode;
|
||||
|
||||
// 3. 读取响应内容
|
||||
string content = await response.Content.ReadAsStringAsync();
|
||||
log.ResponseBody = content;
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
// 如果 T 是 string,直接返回内容,不反序列化
|
||||
if (typeof(T) == typeof(string))
|
||||
return (T)(object)content;
|
||||
|
||||
// 反序列化 JSON
|
||||
return JsonConvert.DeserializeObject<T>(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.ErrorMessage = $"HTTP {response.StatusCode}: {response.ReasonPhrase}";
|
||||
throw new HttpRequestException(log.ErrorMessage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sw.Stop();
|
||||
if (log.DurationMs == 0) log.DurationMs = sw.ElapsedMilliseconds;
|
||||
|
||||
log.StatusCode = 0; // 0 代表网络层面的失败(如断网)
|
||||
log.ErrorMessage = ex.Message;
|
||||
log.ResponseBody = ex.ToString(); // 记录堆栈以便排查
|
||||
|
||||
throw; // 抛出异常供调用方 UI 处理
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 4. 【广播日志】无论成功失败,都触发事件
|
||||
// 使用 Invoke 确保 UI 订阅者能收到
|
||||
OnApiLog?.Invoke(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
SHH.CameraDashboard/Services/StorageService.cs
Normal file
52
SHH.CameraDashboard/Services/StorageService.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SHH.CameraDashboard.Services
|
||||
{
|
||||
public static class StorageService
|
||||
{
|
||||
// 基础目录:C:\Users\Name\AppData\Local\SHH_Dashboard\
|
||||
private static readonly string BaseDir = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
"SHH_CameraDashboard");
|
||||
|
||||
/// <summary>
|
||||
/// 泛型保存:将数据 T 序列化为指定文件名的 JSON
|
||||
/// </summary>
|
||||
public static void Save<T>(T data, string fileName) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(BaseDir)) Directory.CreateDirectory(BaseDir);
|
||||
|
||||
string filePath = Path.Combine(BaseDir, fileName);
|
||||
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
File.WriteAllText(filePath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"存储失败 [{fileName}]: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 泛型读取:从指定 JSON 文件反序列化为数据 T
|
||||
/// </summary>
|
||||
public static T Load<T>(string fileName) where T : class, new()
|
||||
{
|
||||
try
|
||||
{
|
||||
string filePath = Path.Combine(BaseDir, fileName);
|
||||
if (!File.Exists(filePath)) return new T();
|
||||
|
||||
string json = File.ReadAllText(filePath);
|
||||
return JsonConvert.DeserializeObject<T>(json) ?? new T();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"读取失败 [{fileName}]: {ex.Message}");
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user