4943bc16b7
1、重构探测器Hardware.Detector模块,统一设备调用接口,支持多探测器兼容,优化设备连接状态判断逻辑,新增校正帧数可配置功能。 2、优化Varex探测器校正流程,修复内存缓冲区对齐问题,增加指针、分辨率有效性校验,校正期间屏蔽帧回调、自动启停采集,规避SDK冲突与程序崩溃问题。 3、开发通用图像灰度直方图控件,优化资源释放逻辑。
282 lines
12 KiB
C#
282 lines
12 KiB
C#
using System;
|
||
using System.Configuration;
|
||
using System.IO;
|
||
using System.Net;
|
||
using XP.Hardware.Detector.Abstractions;
|
||
using XP.Hardware.Detector.Abstractions.Enums;
|
||
|
||
namespace XP.Hardware.Detector.Config
|
||
{
|
||
/// <summary>
|
||
/// 配置加载器 | Configuration loader
|
||
/// 从 App.config 读取探测器配置并验证参数有效性
|
||
/// </summary>
|
||
public static class ConfigLoader
|
||
{
|
||
/// <summary>
|
||
/// 加载配置 | Load configuration
|
||
/// </summary>
|
||
/// <returns>配置结果 | Configuration result</returns>
|
||
public static DetectorResult<DetectorConfig> LoadConfiguration()
|
||
{
|
||
try
|
||
{
|
||
// 读取探测器类型 | Read detector type
|
||
string typeStr = ConfigurationManager.AppSettings["Detector:Type"];
|
||
if (string.IsNullOrEmpty(typeStr))
|
||
{
|
||
return DetectorResult<DetectorConfig>.Failure("配置文件中未找到 Detector:Type | Detector:Type not found in configuration");
|
||
}
|
||
|
||
if (!Enum.TryParse<DetectorType>(typeStr, true, out var detectorType))
|
||
{
|
||
return DetectorResult<DetectorConfig>.Failure($"无效的探测器类型:{typeStr} | Invalid detector type: {typeStr}");
|
||
}
|
||
|
||
// 根据类型创建配置对象 | Create configuration object based on type
|
||
DetectorConfig config = detectorType switch
|
||
{
|
||
DetectorType.Varex => LoadVarexConfiguration(),
|
||
DetectorType.IRay => LoadIRayConfiguration(),
|
||
DetectorType.Simulated => LoadSimulatedConfiguration(),
|
||
_ => throw new NotSupportedException($"不支持的探测器类型:{detectorType} | Unsupported detector type: {detectorType}")
|
||
};
|
||
|
||
// 加载通用配置 | Load common configuration
|
||
config.Type = detectorType;
|
||
config.IP = ConfigurationManager.AppSettings["Detector:IP"] ?? "127.0.0.1";
|
||
config.Port = int.TryParse(ConfigurationManager.AppSettings["Detector:Port"], out var port) ? port : 5000;
|
||
config.SavePath = ConfigurationManager.AppSettings["Detector:SavePath"] ?? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images");
|
||
config.AutoSave = bool.TryParse(ConfigurationManager.AppSettings["Detector:AutoSave"], out var autoSave) && autoSave;
|
||
|
||
// 加载校正帧数配置(钳位到 1-128)| Load correction frame count config (clamp to 1-128)
|
||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Correction:DarkFrameCount"], out var darkFrames))
|
||
{
|
||
config.DarkCorrectionFrameCount = Math.Clamp(darkFrames, 1, 128);
|
||
}
|
||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Correction:GainFrameCount"], out var gainFrames))
|
||
{
|
||
config.GainCorrectionFrameCount = Math.Clamp(gainFrames, 1, 128);
|
||
}
|
||
|
||
// 验证配置 | Validate configuration
|
||
var validationResult = ValidateConfiguration(config);
|
||
if (!validationResult.IsSuccess)
|
||
{
|
||
return DetectorResult<DetectorConfig>.Failure(validationResult.ErrorMessage);
|
||
}
|
||
|
||
return DetectorResult<DetectorConfig>.Success(config, "配置加载成功 | Configuration loaded successfully");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return DetectorResult<DetectorConfig>.Failure($"加载配置异常 | Load configuration exception: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载 Varex 配置 | Load Varex configuration
|
||
/// </summary>
|
||
private static VarexDetectorConfig LoadVarexConfiguration()
|
||
{
|
||
var config = new VarexDetectorConfig();
|
||
|
||
// 读取 Binning 模式 | Read binning mode
|
||
string binningStr = ConfigurationManager.AppSettings["Detector:Varex:BinningMode"];
|
||
if (!string.IsNullOrEmpty(binningStr) && Enum.TryParse<BinningMode>(binningStr, true, out var binningMode))
|
||
{
|
||
config.BinningMode = binningMode;
|
||
}
|
||
|
||
// 读取增益模式 | Read gain mode
|
||
string gainStr = ConfigurationManager.AppSettings["Detector:Varex:GainMode"];
|
||
if (!string.IsNullOrEmpty(gainStr) && Enum.TryParse<GainMode>(gainStr, true, out var gainMode))
|
||
{
|
||
config.GainMode = gainMode;
|
||
}
|
||
|
||
// 读取曝光时间 | Read exposure time
|
||
if (uint.TryParse(ConfigurationManager.AppSettings["Detector:Varex:ExposureTime"], out var exposureTime))
|
||
{
|
||
config.ExposureTime = exposureTime;
|
||
}
|
||
|
||
// 读取 ROI 参数 | Read ROI parameters
|
||
if (uint.TryParse(ConfigurationManager.AppSettings["Detector:Varex:ROI_X"], out var roiX))
|
||
{
|
||
config.RoiX = roiX;
|
||
}
|
||
if (uint.TryParse(ConfigurationManager.AppSettings["Detector:Varex:ROI_Y"], out var roiY))
|
||
{
|
||
config.RoiY = roiY;
|
||
}
|
||
if (uint.TryParse(ConfigurationManager.AppSettings["Detector:Varex:ROI_Width"], out var roiWidth))
|
||
{
|
||
config.RoiWidth = roiWidth;
|
||
}
|
||
if (uint.TryParse(ConfigurationManager.AppSettings["Detector:Varex:ROI_Height"], out var roiHeight))
|
||
{
|
||
config.RoiHeight = roiHeight;
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载软件模拟探测器配置 | Load simulated detector configuration
|
||
/// </summary>
|
||
private static SimulatedDetectorConfig LoadSimulatedConfiguration()
|
||
{
|
||
var config = new SimulatedDetectorConfig();
|
||
|
||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:Width"], out var w) && w > 0)
|
||
config.Width = w;
|
||
|
||
if (int.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:Height"], out var h) && h > 0)
|
||
config.Height = h;
|
||
|
||
if (double.TryParse(ConfigurationManager.AppSettings["Detector:Simulated:FrameRateFps"],
|
||
System.Globalization.NumberStyles.Any,
|
||
System.Globalization.CultureInfo.InvariantCulture, out var fps) && fps > 0)
|
||
config.FrameRateFps = fps;
|
||
|
||
return config;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载 iRay 配置 | Load iRay configuration
|
||
/// </summary>
|
||
private static IRayDetectorConfig LoadIRayConfiguration()
|
||
{
|
||
var config = new IRayDetectorConfig();
|
||
|
||
// 读取默认增益 | Read default gain
|
||
if (double.TryParse(ConfigurationManager.AppSettings["Detector:IRay:DefaultGain"], out var defaultGain))
|
||
{
|
||
config.DefaultGain = defaultGain;
|
||
}
|
||
|
||
// 读取采集模式 | Read acquisition mode
|
||
string modeStr = ConfigurationManager.AppSettings["Detector:IRay:AcquisitionMode"];
|
||
if (!string.IsNullOrEmpty(modeStr) && Enum.TryParse<AcquisitionMode>(modeStr, true, out var acquisitionMode))
|
||
{
|
||
config.AcquisitionMode = acquisitionMode;
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存探测器参数到 App.config | Save detector parameters to App.config
|
||
/// </summary>
|
||
/// <param name="binningIndex">Binning 索引 | Binning index</param>
|
||
/// <param name="pga">PGA 灵敏度值 | PGA sensitivity value</param>
|
||
/// <param name="frameRate">帧率 | Frame rate</param>
|
||
/// <param name="avgFrames">帧合并数 | Average frame count</param>
|
||
public static void SaveParameters(int binningIndex, int pga, decimal frameRate, int avgFrames)
|
||
{
|
||
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||
|
||
SetAppSetting(config, "Detector:BinningIndex", binningIndex.ToString());
|
||
SetAppSetting(config, "Detector:PGA", pga.ToString());
|
||
SetAppSetting(config, "Detector:FrameRate", frameRate.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||
SetAppSetting(config, "Detector:AvgFrames", avgFrames.ToString());
|
||
|
||
config.Save(ConfigurationSaveMode.Modified);
|
||
ConfigurationManager.RefreshSection("appSettings");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载已保存的探测器参数 | Load saved detector parameters
|
||
/// </summary>
|
||
/// <returns>参数元组(binningIndex, pga, frameRate, avgFrames),加载失败返回 null | Parameter tuple, null if failed</returns>
|
||
public static (int binningIndex, int pga, decimal frameRate, int avgFrames)? LoadSavedParameters()
|
||
{
|
||
try
|
||
{
|
||
string binStr = ConfigurationManager.AppSettings["Detector:BinningIndex"];
|
||
string pgaStr = ConfigurationManager.AppSettings["Detector:PGA"];
|
||
string frStr = ConfigurationManager.AppSettings["Detector:FrameRate"];
|
||
string avgStr = ConfigurationManager.AppSettings["Detector:AvgFrames"];
|
||
|
||
if (string.IsNullOrEmpty(binStr) || string.IsNullOrEmpty(pgaStr) ||
|
||
string.IsNullOrEmpty(frStr) || string.IsNullOrEmpty(avgStr))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
if (int.TryParse(binStr, out var binning) &&
|
||
int.TryParse(pgaStr, out var pgaVal) &&
|
||
decimal.TryParse(frStr, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var fr) &&
|
||
int.TryParse(avgStr, out var avg))
|
||
{
|
||
return (binning, pgaVal, fr, avg);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 忽略加载异常 | Ignore load exception
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置 AppSettings 键值 | Set AppSettings key-value
|
||
/// </summary>
|
||
private static void SetAppSetting(Configuration config, string key, string value)
|
||
{
|
||
if (config.AppSettings.Settings[key] != null)
|
||
{
|
||
config.AppSettings.Settings[key].Value = value;
|
||
}
|
||
else
|
||
{
|
||
config.AppSettings.Settings.Add(key, value);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证配置参数 | Validate configuration parameters
|
||
/// </summary>
|
||
private static DetectorResult ValidateConfiguration(DetectorConfig config)
|
||
{
|
||
// 模拟探测器不需要网络连接,跳过 IP/端口验证 | Simulated detector skips IP/port validation
|
||
if (config.Type != DetectorType.Simulated)
|
||
{
|
||
// 验证 IP 地址 | Validate IP address
|
||
if (!IPAddress.TryParse(config.IP, out _))
|
||
{
|
||
return DetectorResult.Failure($"无效的 IP 地址:{config.IP} | Invalid IP address: {config.IP}");
|
||
}
|
||
|
||
// 验证端口范围 | Validate port range
|
||
if (config.Port < 1 || config.Port > 65535)
|
||
{
|
||
return DetectorResult.Failure($"无效的端口号:{config.Port},必须在 1-65535 之间 | Invalid port: {config.Port}, must be between 1-65535");
|
||
}
|
||
}
|
||
|
||
// 验证存储路径 | Validate save path
|
||
if (string.IsNullOrWhiteSpace(config.SavePath))
|
||
{
|
||
return DetectorResult.Failure("存储路径不能为空 | Save path cannot be empty");
|
||
}
|
||
|
||
try
|
||
{
|
||
// 尝试创建目录以验证路径有效性 | Try to create directory to validate path
|
||
if (!Directory.Exists(config.SavePath))
|
||
{
|
||
Directory.CreateDirectory(config.SavePath);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return DetectorResult.Failure($"无效的存储路径:{config.SavePath},错误:{ex.Message} | Invalid save path: {config.SavePath}, error: {ex.Message}");
|
||
}
|
||
|
||
return DetectorResult.Success("配置验证通过 | Configuration validation passed");
|
||
}
|
||
}
|
||
}
|