捕获图像

This commit is contained in:
zqm
2026-03-17 14:28:07 +08:00
parent ce49e4c736
commit 7cd923b91b
11 changed files with 651 additions and 130 deletions

View File

@@ -85,9 +85,15 @@ namespace XCamera.Core
/// <returns>是否成功初始化</returns>
public bool Initialize(CameraConfig config)
{
if (config == null || !config.IsValid)
if (config == null)
{
throw new ArgumentException("摄像头配置无效");
throw new ArgumentException("摄像头配置为空");
}
if (!config.IsValid)
{
string validationErrors = GetConfigValidationErrors(config);
throw new ArgumentException($"摄像头配置无效: {validationErrors}");
}
lock (_lockObject)
@@ -98,6 +104,34 @@ namespace XCamera.Core
return true;
}
/// <summary>
/// 获取配置验证错误信息
/// </summary>
private string GetConfigValidationErrors(CameraConfig config)
{
var errors = new System.Collections.Generic.List<string>();
if (config.TcpPort <= 0 || config.TcpPort > 65535)
errors.Add($"TCP端口无效: {config.TcpPort}");
if (config.HttpPort <= 0 || config.HttpPort > 65535)
errors.Add($"HTTP端口无效: {config.HttpPort}");
if (config.OnvifPort <= 0 || config.OnvifPort > 65535)
errors.Add($"ONVIF端口无效: {config.OnvifPort}");
if (string.IsNullOrEmpty(config.Username))
errors.Add("用户名为空");
if (config.Channel < 0)
errors.Add($"通道号无效: {config.Channel}");
if (config.LedRegions == null)
errors.Add("LED区域列表为空");
return string.Join(", ", errors);
}
/// <summary>
/// 连接到摄像头
/// </summary>

View File

@@ -31,7 +31,6 @@ namespace XCamera.Core
{
return _config != null ? new CameraConfig
{
IpAddress = _config.IpAddress,
TcpPort = _config.TcpPort,
HttpPort = _config.HttpPort,
OnvifPort = _config.OnvifPort,
@@ -181,10 +180,7 @@ namespace XCamera.Core
lock (_lockObject)
{
if (_config != null)
{
_config.IpAddress = ipAddress;
}
// IP地址现在存储在XCameraManager中不在配置里
}
OnConfigChanged();
@@ -309,10 +305,6 @@ namespace XCamera.Core
// 简单的JSON解析实现
// 这里使用基本的字符串解析实际项目中建议使用Newtonsoft.Json
// 提取IP地址
var ipMatch = System.Text.RegularExpressions.Regex.Match(jsonContent, @"""IpAddress""\s*:\s*""([^""]+)""");
if (ipMatch.Success) config.IpAddress = ipMatch.Groups[1].Value;
// 提取端口
var tcpPortMatch = System.Text.RegularExpressions.Regex.Match(jsonContent, @"""TcpPort""\s*:\s*(\d+)");
if (tcpPortMatch.Success) config.TcpPort = int.Parse(tcpPortMatch.Groups[1].Value);
@@ -421,7 +413,6 @@ namespace XCamera.Core
{
var sb = new StringBuilder();
sb.AppendLine("{");
sb.AppendLine($" \"IpAddress\": \"{config.IpAddress}\",");
sb.AppendLine($" \"TcpPort\": {config.TcpPort},");
sb.AppendLine($" \"HttpPort\": {config.HttpPort},");
sb.AppendLine($" \"OnvifPort\": {config.OnvifPort},");
@@ -457,13 +448,12 @@ namespace XCamera.Core
/// <summary>
/// 创建默认配置
/// </summary>
private bool CreateDefaultConfig(string configFilePath)
public bool CreateDefaultConfig(string configFilePath)
{
try
{
var defaultConfig = new CameraConfig
{
IpAddress = "192.168.100.10",
TcpPort = 34567,
HttpPort = 8888,
OnvifPort = 8899,

View File

@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace XCamera.Core
@@ -8,6 +9,30 @@ namespace XCamera.Core
/// </summary>
public static class XCloudSdkWrapper
{
// Windows API - 设置DLL搜索路径
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
/// <summary>
/// 设置XCloudSDK.dll的搜索路径
/// </summary>
/// <param name="dllPath">DLL所在目录路径</param>
/// <returns>是否成功</returns>
public static bool SetDllSearchPath(string dllPath)
{
try
{
if (Directory.Exists(dllPath))
{
return SetDllDirectory(dllPath);
}
return false;
}
catch
{
return false;
}
}
// 回调函数委托
public delegate int XCloudSdkMessageCallback(
int hObject,

View File

@@ -8,11 +8,6 @@ namespace XCamera.Models
/// </summary>
public class CameraConfig
{
/// <summary>
/// IP地址
/// </summary>
public string IpAddress { get; set; }
/// <summary>
/// TCP端口固定34567
/// </summary>
@@ -55,13 +50,10 @@ namespace XCamera.Models
{
get
{
return !string.IsNullOrEmpty(IpAddress) &&
IsValidIpAddress(IpAddress) &&
TcpPort > 0 && TcpPort <= 65535 &&
return TcpPort > 0 && TcpPort <= 65535 &&
HttpPort > 0 && HttpPort <= 65535 &&
OnvifPort > 0 && OnvifPort <= 65535 &&
!string.IsNullOrEmpty(Username) &&
!string.IsNullOrEmpty(Password) &&
Channel >= 0 &&
LedRegions != null;
}
@@ -72,7 +64,6 @@ namespace XCamera.Models
/// </summary>
public CameraConfig()
{
IpAddress = "192.168.100.10";
TcpPort = 34567;
HttpPort = 8888;
OnvifPort = 8899;
@@ -104,11 +95,11 @@ namespace XCamera.Models
}
/// <summary>
/// 获取设备ID基于IP地址
/// 获取设备ID基于配置信息
/// </summary>
public string GetDeviceId()
{
return $"DEV_{IpAddress.Replace(".", "_")}";
return $"DEV_{TcpPort}_{Channel}";
}
/// <summary>
@@ -116,7 +107,7 @@ namespace XCamera.Models
/// </summary>
public string GetConnectionString()
{
return $"{{\"IP\":\"{IpAddress}\",\"Port\":{TcpPort}}}";
return $"{{\"Port\":{TcpPort}}}";
}
}
}

View File

@@ -59,6 +59,25 @@ namespace XCamera.Models
}
}
/// <summary>
/// 信息事件参数
/// </summary>
public class InfoEventArgs : EventArgs
{
/// <summary>
/// 信息消息
/// </summary>
public string InfoMessage { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public InfoEventArgs(string infoMessage)
{
InfoMessage = infoMessage;
}
}
/// <summary>
/// 错误事件参数
/// </summary>
@@ -77,4 +96,49 @@ namespace XCamera.Models
ErrorMessage = errorMessage;
}
}
/// <summary>
/// 图像捕获事件参数
/// </summary>
public class ImageCapturedEventArgs : EventArgs
{
/// <summary>
/// 捕获的图像
/// </summary>
public System.Drawing.Image Image { get; set; }
/// <summary>
/// 图像文件名(临时文件)
/// </summary>
public string ImageFilePath { get; set; }
/// <summary>
/// 捕获时间
/// </summary>
public DateTime CaptureTime { get; set; }
/// <summary>
/// LED状态列表可选
/// </summary>
public System.Collections.Generic.List<LedStatus> LedStatuses { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public ImageCapturedEventArgs(System.Drawing.Image image, string imageFilePath)
{
Image = image;
ImageFilePath = imageFilePath;
CaptureTime = DateTime.Now;
LedStatuses = new System.Collections.Generic.List<LedStatus>();
}
/// <summary>
/// 带LED状态的构造函数
/// </summary>
public ImageCapturedEventArgs(System.Drawing.Image image, string imageFilePath, System.Collections.Generic.List<LedStatus> ledStatuses) : this(image, imageFilePath)
{
LedStatuses = ledStatuses ?? new System.Collections.Generic.List<LedStatus>();
}
}
}

View File

@@ -60,6 +60,24 @@ namespace XCamera
remove { Manager.CaptureStateChanged -= value; }
}
/// <summary>
/// 图像捕获事件
/// </summary>
public static event EventHandler<ImageCapturedEventArgs> ImageCaptured
{
add { Manager.ImageCaptured += value; }
remove { Manager.ImageCaptured -= value; }
}
/// <summary>
/// 信息事件
/// </summary>
public static event EventHandler<InfoEventArgs> InfoOccurred
{
add { Manager.InfoOccurred += value; }
remove { Manager.InfoOccurred -= value; }
}
/// <summary>
/// 错误事件
/// </summary>
@@ -104,13 +122,23 @@ namespace XCamera
}
/// <summary>
/// 2. 设置配置文件路径
/// 2. 设置配置目录路径
/// </summary>
/// <param name="configFilePath">配置文件路径</param>
/// <param name="configPath">配置目录路径</param>
/// <returns>是否成功设置</returns>
public static bool SetConfigFile(string configFilePath)
public static bool SetConfigPath(string configPath)
{
return Manager.SetConfigFile(configFilePath);
return Manager.SetConfigPath(configPath);
}
/// <summary>
/// 2.1 设置摄像头IP地址
/// </summary>
/// <param name="ipAddress">IP地址</param>
/// <returns>是否成功设置</returns>
public static bool SetIP(string ipAddress)
{
return Manager.UpdateIpAddress(ipAddress);
}
/// <summary>

View File

@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using XCamera.Models;
using XCamera.Core;
using XCamera.Vision;
using ErrorEventArgs = XCamera.Models.ErrorEventArgs;
namespace XCamera
{
@@ -23,6 +25,7 @@ namespace XCamera
private bool _shouldStopCapture = false;
private readonly object _lockObject = new object();
private int _captureInterval = 1000;
private string _cameraIpAddress = "192.168.100.10"; // 摄像头IP地址
/// <summary>
/// LED状态更新事件
@@ -39,6 +42,16 @@ namespace XCamera
/// </summary>
public event EventHandler<CaptureStateEventArgs> CaptureStateChanged;
/// <summary>
/// 图像捕获事件
/// </summary>
public event EventHandler<ImageCapturedEventArgs> ImageCaptured;
/// <summary>
/// 信息事件(用于显示正常信息)
/// </summary>
public event EventHandler<InfoEventArgs> InfoOccurred;
/// <summary>
/// 错误事件
/// </summary>
@@ -87,9 +100,21 @@ namespace XCamera
/// <summary>
/// 1. 初始化
/// </summary>
/// <param name="configFilePath">配置文件路径</param>
/// <summary>
/// 初始化管理器仅初始化SDK和基础功能不加载配置
/// </summary>
/// <returns>是否成功初始化</returns>
public bool Initialize(string configFilePath = null)
public bool Initialize()
{
return Initialize(null);
}
/// <summary>
/// 初始化管理器(带配置文件路径)
/// </summary>
/// <param name="configFilePath">配置文件路径为null则不加载配置</param>
/// <returns>是否成功初始化</returns>
public bool Initialize(string configFilePath)
{
try
{
@@ -99,6 +124,14 @@ namespace XCamera
Cleanup();
}
// 设置DLL搜索路径
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
string dllPath = System.IO.Path.Combine(baseDirectory, "lib", "XCloudSDK");
if (System.IO.Directory.Exists(dllPath))
{
XCloudSdkWrapper.SetDllSearchPath(dllPath);
}
// 初始化SDK
string initParam = "{\"LogLevel\":8,\"TempPath\":\"\",\"ConfigPath\":\"\",\"PlatUUID\":\"\",\"PlatAppKey\":\"\",\"PlatAppSecret\":\"\",\"PlatMovedCard\":0,\"ServerIP\":\"\",\"ServerPort\":34567,\"InitType\":0}";
int result = XCloudSdkWrapper.XCloudSDK_Init(initParam);
@@ -115,7 +148,7 @@ namespace XCamera
throw new Exception($"注册回调函数失败,错误码: {userHandle}");
}
// 加载配置
// 仅在提供了配置文件路径时加载配置
if (!string.IsNullOrEmpty(configFilePath))
{
_configManager.LoadFromFile(configFilePath);
@@ -137,11 +170,11 @@ namespace XCamera
}
/// <summary>
/// 2. 设置配置文件路径
/// 2. 设置配置目录路径
/// </summary>
/// <param name="configFilePath">配置文件路径</param>
/// <param name="configPath">配置目录路径</param>
/// <returns>是否成功设置</returns>
public bool SetConfigFile(string configFilePath)
public bool SetConfigPath(string configPath)
{
if (!_isInitialized)
{
@@ -151,12 +184,38 @@ namespace XCamera
try
{
_configManager.LoadFromFile(configFilePath);
if (string.IsNullOrEmpty(configPath))
{
OnErrorOccurred("配置目录路径不能为空");
return false;
}
// 查找目录中的配置文件
if (!Directory.Exists(configPath))
{
OnErrorOccurred($"配置目录不存在: {configPath}");
return false;
}
// 查找LedArea.json配置文件
string ledAreaConfigFile = Path.Combine(configPath, "LedArea.json");
if (!File.Exists(ledAreaConfigFile))
{
// 如果没有LedArea.json创建默认配置
_configManager.CreateDefaultConfig(ledAreaConfigFile);
OnInfoOccurred($"配置目录中没有找到LedArea.json已创建默认配置文件: {ledAreaConfigFile}");
}
else
{
// 加载LedArea.json配置文件
_configManager.LoadFromFile(ledAreaConfigFile);
OnInfoOccurred($"成功加载配置文件: {ledAreaConfigFile}");
}
return true;
}
catch (Exception ex)
{
OnErrorOccurred($"设置配置文件失败: {ex.Message}");
OnErrorOccurred($"设置配置目录失败: {ex.Message}");
return false;
}
}
@@ -289,13 +348,42 @@ namespace XCamera
return new List<LedStatus>();
}
// 加载图像并触发图像捕获事件
try
{
using (var image = System.Drawing.Image.FromFile(tempFile))
{
// 触发图像捕获事件
OnImageCaptured((System.Drawing.Image)image.Clone(), tempFile);
}
}
catch (Exception imgEx)
{
OnErrorOccurred($"图像加载失败: {imgEx.Message}");
}
try
{
// 加载图像到处理器
_imageProcessor.LoadImage(tempFile);
// 检测LED状态
return _ledDetector.DetectLedStates(CurrentConfig.LedRegions);
var ledStatuses = _ledDetector.DetectLedStates(CurrentConfig.LedRegions);
// 更新图像事件包含LED状态信息
try
{
using (var image = System.Drawing.Image.FromFile(tempFile))
{
OnImageCaptured((System.Drawing.Image)image.Clone(), tempFile, ledStatuses);
}
}
catch (Exception imgEx)
{
OnErrorOccurred($"图像加载失败: {imgEx.Message}");
}
return ledStatuses;
}
finally
{
@@ -425,7 +513,33 @@ namespace XCamera
try
{
_configManager.UpdateIpAddress(ipAddress);
if (string.IsNullOrEmpty(ipAddress))
{
throw new ArgumentException("IP地址不能为空");
}
// 验证IP地址格式
var parts = ipAddress.Split('.');
if (parts.Length != 4)
{
throw new ArgumentException("IP地址格式不正确");
}
foreach (var part in parts)
{
if (!int.TryParse(part, out int num) || num < 0 || num > 255)
{
throw new ArgumentException("IP地址格式不正确");
}
}
lock (_lockObject)
{
_cameraIpAddress = ipAddress;
}
// 使用信息事件而不是错误事件
OnInfoOccurred($"IP地址已更新: {ipAddress}");
return true;
}
catch (Exception ex)
@@ -644,6 +758,30 @@ namespace XCamera
ErrorOccurred?.Invoke(this, new ErrorEventArgs(error));
}
/// <summary>
/// 触发信息事件
/// </summary>
private void OnInfoOccurred(string info)
{
InfoOccurred?.Invoke(this, new InfoEventArgs(info));
}
/// <summary>
/// 触发图像捕获事件
/// </summary>
private void OnImageCaptured(System.Drawing.Image image, string imageFilePath)
{
OnImageCaptured(image, imageFilePath, new List<LedStatus>());
}
/// <summary>
/// 触发图像捕获事件带LED状态
/// </summary>
private void OnImageCaptured(System.Drawing.Image image, string imageFilePath, List<LedStatus> ledStatuses)
{
ImageCaptured?.Invoke(this, new ImageCapturedEventArgs(image, imageFilePath, ledStatuses));
}
/// <summary>
/// 释放资源
/// </summary>