You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
604 lines
22 KiB
604 lines
22 KiB
using Newtonsoft.Json; |
|
using System; |
|
using System.Configuration; |
|
using System.Data; |
|
using System.IO; |
|
using System.Net.Http; |
|
using System.Text; |
|
using log4net; |
|
using BrilliantSightClient.Model; |
|
using BrilliantSightClient.Model.Entity.ApiEntity; |
|
using BrilliantSightClient.Model.Extension; |
|
using BrilliantSightClient.Model.Helper; |
|
|
|
namespace BrilliantSightClient.Model.Services |
|
{ |
|
/// <summary> |
|
/// SOC 客户端服务类,用于与远程服务器进行交互,包括启动图片收集任务、获取图片、获取采集状态等操作。 |
|
/// </summary> |
|
public class SOCClientService |
|
{ |
|
// Log地址 |
|
|
|
|
|
private static readonly ILog Logger = LogManager.GetLogger(typeof(SOCClientService)); |
|
|
|
/// <summary> |
|
/// 基础URL,用于构建完整的API请求地址。 |
|
/// </summary> |
|
private readonly string? _baseUrl; |
|
|
|
/// <summary> |
|
/// 认证令牌,用于HTTP请求的认证。 |
|
/// </summary> |
|
private readonly string _authToken; |
|
|
|
private static SOCClientService _service; |
|
|
|
public static SOCClientService Service { |
|
get |
|
{ |
|
if (_service == null) |
|
_service = new SOCClientService(); |
|
return _service; |
|
} |
|
|
|
} |
|
|
|
private bool GenImage; |
|
/// <summary> |
|
/// 构造函数,初始化基础URL和认证令牌。 |
|
/// </summary> |
|
private SOCClientService() |
|
{ |
|
_baseUrl = ConfigurationManager.AppSettings["BaseUrl"]; |
|
_authToken = "your_basic_auth_token"; |
|
} |
|
|
|
/// <summary> |
|
/// 发送GET请求的通用方法。 |
|
/// </summary> |
|
/// <param name="url">请求的完整URL</param> |
|
/// <returns>HTTP响应</returns> |
|
private async Task<HttpSendResult<T>> SendGetRequestAsync<T>(string url, string sendCode = "", JsonSerializerSettings settings = null)where T : ResponseStatus |
|
{ |
|
if (sendCode.IsNullOrEmpty()) |
|
{ |
|
sendCode = url.GenerateSign(); |
|
} |
|
using (var client = new HttpClient()) |
|
{ |
|
Logger.Info($"[SendCode={sendCode}]Request sent to URL: {url}"); |
|
client.DefaultRequestHeaders.Add("Authorization", "Basic " + _authToken); |
|
client.Timeout = new TimeSpan(0, 0, 30); |
|
HttpResponseMessage result = await client.GetAsync(url); |
|
|
|
// 提前读取内容并存储 |
|
string responseBody = await result.Content.ReadAsStringAsync(); |
|
int statusCode = (int)result.StatusCode; |
|
// 记录日志 |
|
Logger.Info($"[SendCode={sendCode}]Response: Status={statusCode}, Body={responseBody}"); |
|
var tempEntity = JsonConvert.DeserializeObject<T>(responseBody, settings); |
|
if (StatusCodes.PreviousTaskIncomplete.Equals(tempEntity.Status)) |
|
{ |
|
Logger.Info($"S009 请求重试"); |
|
await Task.Delay(500); |
|
return await SendGetRequestAsync<T>(url, sendCode); |
|
} |
|
|
|
return new HttpSendResult<T>() |
|
{ |
|
StatusCode = statusCode, |
|
Content = tempEntity, |
|
}; |
|
} |
|
} |
|
private async Task<HttpResponseMessage> SendGetRequestImageAsync(string url, string sendCode = "") |
|
{ |
|
if (sendCode.IsNullOrEmpty()) |
|
{ |
|
sendCode = url.GenerateSign(); |
|
} |
|
using (var client = new HttpClient()) |
|
{ |
|
Logger.Info($"[SendCode={sendCode}]Request Image Download URL: {url}"); |
|
client.DefaultRequestHeaders.Add("Authorization", "Basic " + _authToken); |
|
var data = await client.GetAsync(url); |
|
Logger.Info($"[SendCode={sendCode}]Request Image Downloaded {data.StatusCode}"); |
|
return data; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 启动图片收集任务。 |
|
/// </summary> |
|
/// <returns>任务状态</returns> |
|
public async Task<SocResultEntity> CollectImagesAsync() |
|
{ |
|
try |
|
{ |
|
GenImage = true; |
|
// 光照度和半圆 |
|
int lightLevel = 0; |
|
string halfCircle = string.Empty; |
|
// 查询光照度和半圆配置 |
|
string sql = $"SELECT KEY, VALUE FROM CUTTER_CONFIG "; |
|
DataTable table = DataBaseHelper.ExecuteQuery(sql); |
|
|
|
if (table == null || table.Rows.Count == 0) |
|
{ |
|
throw new Exception("No data found for the specified keys."); |
|
} |
|
StringBuilder sbParams = new StringBuilder(); |
|
foreach (DataRow row in table.Rows) |
|
{ |
|
string key = row["Key"].ToString() ?? string.Empty; |
|
string value = row["Value"].ToString() ?? string.Empty; |
|
|
|
// if (key == "light_level" && int.TryParse(value, out int parsedLightLevel)) |
|
// { |
|
// lightLevel = parsedLightLevel; // 光照度 |
|
// } |
|
// else if (key == "half_circle") |
|
// { |
|
// halfCircle = value; // 半圆 |
|
// } |
|
sbParams.Append($"{key}={value}&"); |
|
} |
|
|
|
// string url = $"{_baseUrl}/collect_images?light_level={lightLevel}&half_circle={halfCircle}"; |
|
string url = $"{_baseUrl}/collect_images?{sbParams.ToString().Substring(0, sbParams.ToString().Length - 1)}"; |
|
|
|
var response = await SendGetRequestAsync<ResponseStatus>(url); |
|
|
|
if (response.StatusCode != 200) |
|
{ |
|
return new SocResultEntity { Status = StatusCodes.DeviceNotFound, Images = new List<string>() }; |
|
} |
|
|
|
var result = response.Content; |
|
|
|
if (result == null) |
|
{ |
|
return new SocResultEntity { Status = StatusCodes.DeviceNotFound, Images = new List<string>() }; |
|
} |
|
|
|
return new SocResultEntity |
|
{ Status = result.Status, Images = new List<string>(), DeviceId = result.device_id }; |
|
|
|
} |
|
catch (Exception ex) |
|
{ |
|
// 记录日志或进行其他处理 |
|
Console.WriteLine($"Error in DoSoc: {ex.Message}"); |
|
Logger.Warn($"Error in DoSoc: {ex.Message}"); |
|
// 或者使用日志框架记录日志 |
|
// logger.LogError(ex, "Error in DoSoc method."); |
|
return new SocResultEntity |
|
{ Status = StatusCodes.DeviceNotFound, Images = new List<string>(), DeviceId = "" }; |
|
} |
|
finally |
|
{ |
|
GenImage = false; |
|
} |
|
} |
|
/// <summary> |
|
/// 启动图片收集任务。 |
|
/// </summary> |
|
/// <returns>任务状态</returns> |
|
public Task<HttpSendResult<ResponseStatus>> CollectImagesAsyncNotAwait() |
|
{ |
|
try |
|
{ |
|
GenImage = true; |
|
// 光照度和半圆 |
|
int lightLevel = 0; |
|
string halfCircle = string.Empty; |
|
// 查询光照度和半圆配置 |
|
string sql = $"SELECT KEY, VALUE FROM CUTTER_CONFIG "; |
|
DataTable table = DataBaseHelper.ExecuteQuery(sql); |
|
|
|
if (table == null || table.Rows.Count == 0) |
|
{ |
|
throw new Exception("No data found for the specified keys."); |
|
} |
|
|
|
StringBuilder sbParams = new StringBuilder(); |
|
foreach (DataRow row in table.Rows) |
|
{ |
|
string key = row["Key"].ToString() ?? string.Empty; |
|
string value = row["Value"].ToString() ?? string.Empty; |
|
|
|
// if (key == "light_level" && int.TryParse(value, out int parsedLightLevel)) |
|
// { |
|
// lightLevel = parsedLightLevel; // 光照度 |
|
// } |
|
// else if (key == "half_circle") |
|
// { |
|
// halfCircle = value; // 半圆 |
|
// } |
|
sbParams.Append($"{key}={value}&"); |
|
} |
|
|
|
// string url = $"{_baseUrl}/collect_images?light_level={lightLevel}&half_circle={halfCircle}"; |
|
string url = $"{_baseUrl}/collect_images?{sbParams.ToString().Substring(0, sbParams.ToString().Length - 1)}"; |
|
|
|
var result = SendGetRequestAsync<ResponseStatus>(url); |
|
|
|
return result; |
|
|
|
|
|
} |
|
catch (Exception ex) |
|
{ |
|
// 记录日志或进行其他处理 |
|
Console.WriteLine($"Error in DoSoc: {ex.Message}"); |
|
Logger.Warn($"Error in DoSoc: {ex.Message}"); |
|
// 或者使用日志框架记录日志 |
|
// logger.LogError(ex, "Error in DoSoc method."); |
|
return null; |
|
} |
|
finally |
|
{ |
|
GenImage = false; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 获取指定索引的图片。 |
|
/// </summary> |
|
/// <param name="savePath">保存图片路径</param> |
|
/// <returns>图片的字节数组</returns> |
|
public async Task<List<string>> RetrieveImageAsync(string? savePath, CancellationToken token = default) |
|
{ |
|
|
|
List<string> imageNames = new List<string>(); |
|
// 读取图片接口 |
|
int imageIndex = 0; |
|
int imageTotal = ConfigurationHelper.ReadConfigValueToInteger("DetectImageTotal", 100); |
|
while (true) |
|
{ |
|
if (imageIndex >= imageTotal) |
|
{ |
|
return imageNames; |
|
} |
|
|
|
string url = $"{_baseUrl}/retrieve_image/{imageIndex}"; |
|
string sendCode = url.GenerateSign(); |
|
try |
|
{ |
|
var response = await SendGetRequestImageAsync(url, sendCode); |
|
int status = (int)response.StatusCode; |
|
|
|
token.ThrowIfCancellationRequested(); |
|
switch (status) |
|
{ |
|
case 200: |
|
byte[] imageBytes = await response.Content.ReadAsByteArrayAsync(); |
|
|
|
// 获取 Content-Type 头以确定图片格式 |
|
string contentType = response.Content.Headers.ContentType.MediaType; |
|
string fileExtension = GetFileExtension(contentType); |
|
|
|
string fileName = Path.Combine(savePath, $"image_{imageIndex}{fileExtension}"); |
|
// 图片名称List |
|
imageNames.Add(Path.GetFileName(fileName)); |
|
// 保存图片 |
|
await File.WriteAllBytesAsync(fileName, imageBytes); |
|
imageIndex++; |
|
break; |
|
case 425: |
|
// 请求被锁定,等待一段时间后重试 |
|
await Task.Delay(500); |
|
break; |
|
case 410: |
|
// 资源已被永久删除,跳过当前索引 |
|
imageIndex++; |
|
break; |
|
|
|
case 404: |
|
// 资源未找到,结束循环 |
|
Logger.Info($"[SendCode={sendCode}] Image Not Found."); |
|
break; |
|
//return imageNames; |
|
default: |
|
// 其他状态码,记录警告并继续 |
|
Logger.Warn($"[SendCode={sendCode}]Unexpected status code: {status} for URL: {url}"); |
|
imageIndex++; |
|
break; |
|
} |
|
} |
|
catch (HttpRequestException ex) |
|
{ |
|
// 捕获HTTP请求异常并记录错误信息 |
|
Logger.Error($"[SendCode={sendCode}]HTTP request failed for URL: {url}, Exception: {ex.Message}"); |
|
return imageNames; |
|
} |
|
catch (Exception ex) |
|
{ |
|
// 捕获其他异常并记录错误信息,结束循环 |
|
Logger.Error( |
|
$"[SendCode={sendCode}]An unexpected error occurred for URL: {url}, Exception: {ex.Message}"); |
|
return imageNames; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 获取图片采集状态。 |
|
/// </summary> |
|
/// <returns>采集状态</returns> |
|
public async Task<string> CollectStatusAsync() |
|
{ |
|
string url = $"{_baseUrl}/collect_status"; |
|
|
|
try |
|
{ |
|
var response = await SendGetRequestAsync<ResponseStatus>(url); |
|
Logger.Debug($" CollectStatusAsync url :{ url} "); |
|
if (response.StatusCode == 200) |
|
{ |
|
|
|
ResponseStatus result = response.Content; |
|
Logger.Debug($" CollectStatusAsync result :{ result.ToSafeAbundantString()} "); |
|
return result.Status; |
|
} |
|
|
|
return StatusCodes.DeviceNotFound; |
|
} |
|
catch (HttpRequestException ex) |
|
{ |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
catch (JsonException ex) |
|
{ |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
catch (Exception ex) |
|
{ |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 处理图片收集、保存和状态检查。 |
|
/// </summary> |
|
/// <returns>操作结果</returns> |
|
public async Task<SocResultEntity> ProcessImageCollectionAsync() |
|
{ |
|
try |
|
{ |
|
await OpenPump(true); |
|
// SOC接口 |
|
string? savePath = ConfigurationManager.AppSettings["ImageFileBasePath"]; |
|
// 清理 savePath 文件夹 |
|
if (Directory.Exists(savePath)) |
|
{ |
|
Directory.Delete(savePath, true); |
|
} |
|
Directory.CreateDirectory(savePath); |
|
|
|
|
|
// 启动任务接口 |
|
SocResultEntity entity = await CollectImagesAsync(); |
|
// 成功 |
|
Logger.Debug($"entity :{entity.Status} {entity.ToString()} "); |
|
if (entity.Status != StatusCodes.Success) |
|
{ |
|
// 启动任务失败 |
|
return new SocResultEntity { Status = entity.Status, Images = new List<string>() }; |
|
} |
|
// 读取图片接口 |
|
List<string> imageNames = await RetrieveImageAsync(savePath); |
|
|
|
if (imageNames.Count == 0) |
|
{ |
|
Logger.Debug("imageNames.Count == 0"); |
|
// 图片文件读取失败 |
|
return new SocResultEntity { Status = StatusCodes.ImageFileReadFailure, Images = new List<string>() }; |
|
} |
|
return new SocResultEntity { Status = "S000", Images = imageNames, DeviceId = entity.DeviceId}; |
|
} |
|
catch (Exception e) |
|
{ |
|
// 日志记录 |
|
// logger.Error(e, "发生异常"); |
|
string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 发生异常: {e.Message}{Environment.NewLine}"; |
|
Logger.Error(logMessage); |
|
return new SocResultEntity { Status = StatusCodes.DeviceNotFound, Images = new List<string>() }; |
|
} |
|
finally |
|
{ |
|
await OpenPump(false); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 根据给定的 MIME 类型获取对应的文件扩展名。 |
|
/// </summary> |
|
/// <param name="contentType">HTTP 响应中的 Content-Type 头字段,表示内容的 MIME 类型。</param> |
|
/// <returns>与 MIME 类型对应的文件扩展名。</returns> |
|
/// <exception cref="InvalidOperationException">当传入的 MIME 类型不受支持时抛出此异常。</exception> |
|
private string GetFileExtension(string contentType) |
|
{ |
|
switch (contentType.ToLower()) |
|
{ |
|
case "image/bmp": |
|
return ".bmp"; |
|
case "image/jpg": |
|
return ".jpg"; |
|
case "image/png": |
|
return ".png"; |
|
default: |
|
throw new InvalidOperationException($"Unsupported content type: {contentType}"); |
|
} |
|
} |
|
|
|
public async Task<string> CutRevolve(double angle) |
|
{ |
|
// await OpenPump(true); |
|
int param = (int)angle * 100; |
|
string url = $"{_baseUrl}/rotate_to?angle={param}"; |
|
// if(GenImage) return StatusCodes.DeviceNotFound; |
|
GenImage = true; |
|
try |
|
{ |
|
var response = await SendGetRequestAsync<ResponseStatus>(url); |
|
Logger.Debug(url); |
|
if (response.StatusCode == 200) |
|
{ |
|
var result = response.Content; |
|
Logger.Debug($"Rotate Angle : {result.ToSafeAbundantString()} "); |
|
return result.Status; |
|
} |
|
|
|
return StatusCodes.DeviceNotFound; |
|
} |
|
catch (Exception e) |
|
{ |
|
string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 发生异常: {e.Message}{Environment.NewLine}"; |
|
Logger.Error(logMessage); |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
finally |
|
{ |
|
GenImage = false; |
|
// await OpenPump(true); |
|
} |
|
} |
|
|
|
public async Task<string> OpenPump(bool isOpen) |
|
{ |
|
string runModel = ConfigurationHelper.ReadConfigValue("RunModel") ?? "10"; |
|
if ("1" == runModel || "0" == runModel) |
|
{ |
|
return StatusCodes.Success; |
|
} |
|
|
|
// if(GenImage) return StatusCodes.DeviceNotFound; |
|
Logger.Info($"气泵开关请求发起{isOpen}"); |
|
int param = isOpen ? 1 : 0 ; |
|
string url = $"{_baseUrl}/set_pump?on={param}"; |
|
Logger.Debug(url); |
|
try |
|
{ |
|
var response = await SendGetRequestAsync<ResponseStatus>(url); |
|
|
|
if (response.StatusCode == 200) |
|
{ |
|
if (response.Content.Status != StatusCodes.Success) |
|
{ |
|
return await OpenPump(isOpen); |
|
} |
|
var result = response.Content; |
|
Logger.Debug($"Set Pump : {result.ToSafeAbundantString()} "); |
|
return result.Status; |
|
} |
|
Logger.Info($"气泵开关请求发起完毕"); |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
catch (Exception e) |
|
{ |
|
string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 发生异常: {e.Message}{Environment.NewLine}"; |
|
Logger.Error(logMessage); |
|
return StatusCodes.DeviceNotFound; |
|
} |
|
} |
|
|
|
public async Task<GpioStatus> GetGpioStatus() |
|
{ |
|
string runModel = ConfigurationHelper.ReadConfigValue("RunModel") ?? "10"; |
|
if ("1" == runModel || "0" == runModel) |
|
{ |
|
return GpioStatus.IsClose(); |
|
} |
|
string url = $"{_baseUrl}/gpio_check"; |
|
Logger.Info($"舱门状态检查开始:{url}"); |
|
try |
|
{ |
|
var response = await SendGetRequestAsync<GpioStatus>(url); |
|
|
|
if (response.StatusCode == 200) |
|
{ |
|
var result = response.Content; |
|
Logger.Info($"Set Pump : {result.ToSafeAbundantString()} "); |
|
return result; |
|
} |
|
Logger.Info($"舱门状态检查完毕,接口status非200视为关闭"); |
|
return GpioStatus.Default(); |
|
} |
|
catch (Exception e) |
|
{ |
|
string logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] 发生异常: {e.Message}{Environment.NewLine}"; |
|
Logger.Error(logMessage); |
|
return GpioStatus.Default(); |
|
} |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// 响应状态类,用于解析服务器返回的状态信息。 |
|
/// </summary> |
|
public class ResponseStatus |
|
{ |
|
/// <summary> |
|
/// 状态码 |
|
/// </summary> |
|
public string Status { get; set; } |
|
|
|
/// <summary> |
|
/// 状态消息 |
|
/// </summary> |
|
public string Message { get; set; } |
|
|
|
/// <summary> |
|
/// 机器号 |
|
/// </summary> |
|
public string device_id { get; set; } |
|
} |
|
|
|
public class GpioStatus : ResponseStatus |
|
{ |
|
public int Value1 { get; set; } |
|
public int Value2 { get; set; } |
|
|
|
public bool LensGpioIsOpen() |
|
{ |
|
return Value1 == 48; |
|
} |
|
|
|
public bool DiamondGpioIsOpen() |
|
{ |
|
return Value2 == 48; |
|
} |
|
public bool LensGpioIsClose() |
|
{ |
|
return Value1 == 49; |
|
} |
|
|
|
public bool DiamondGpioIsClose() |
|
{ |
|
return Value2 == 49; |
|
} |
|
|
|
public static GpioStatus Default() |
|
{ |
|
return new GpioStatus |
|
{ |
|
Status = "S000", |
|
Value1 = 48, |
|
Value2 = 48 |
|
}; |
|
} |
|
public static GpioStatus IsClose() |
|
{ |
|
return new GpioStatus |
|
{ |
|
Status = "S000", |
|
Value1 = 49, |
|
Value2 = 49 |
|
}; |
|
} |
|
} |
|
}
|
|
|