diff --git a/Windows/CS/Framework4.0/Toprie/Toprie.sln b/Windows/CS/Framework4.0/Toprie/Toprie.sln
index 60116d7..6ae5176 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie.sln
+++ b/Windows/CS/Framework4.0/Toprie/Toprie.sln
@@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Toprie", "Toprie\Toprie.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{C309F3A2-0E77-42E4-BE3D-1448E7DFA433}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ToprieA8", "..\..\Standard1.0\ToprieA8\ToprieA8.csproj", "{D47E2A7E-9E74-4541-B828-885267F17AE8}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,10 +21,6 @@ Global
{C309F3A2-0E77-42E4-BE3D-1448E7DFA433}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C309F3A2-0E77-42E4-BE3D-1448E7DFA433}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C309F3A2-0E77-42E4-BE3D-1448E7DFA433}.Release|Any CPU.Build.0 = Release|Any CPU
- {D47E2A7E-9E74-4541-B828-885267F17AE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D47E2A7E-9E74-4541-B828-885267F17AE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D47E2A7E-9E74-4541-B828-885267F17AE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D47E2A7E-9E74-4541-B828-885267F17AE8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
new file mode 100644
index 0000000..990c155
--- /dev/null
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs
@@ -0,0 +1,1314 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Net.NetworkInformation;
+using System.Net.Sockets;
+using System.IO;
+using System.Drawing;
+using System.Diagnostics;
+using System.Net;
+using System.Text;
+
+namespace JoyD.Windows.CS.Toprie
+{
+ ///
+ /// 连接状态枚举
+ ///
+ public enum ConnectionStatus
+ {
+ Disconnected,
+ Connecting,
+ Connected,
+ Reconnecting
+ }
+
+ ///
+ /// 图像模式枚举
+ ///
+ public enum ImageMode
+ {
+ ///
+ /// 热图模式(红外热成像)
+ ///
+ Thermal,
+ ///
+ /// 全彩模式(可见光图像)
+ ///
+ FullColor
+ }
+
+ ///
+ /// 连接状态变更事件参数
+ ///
+ public class ConnectionStatusChangedEventArgs : EventArgs
+ {
+ public ConnectionStatus NewStatus { get; set; }
+ public ConnectionStatus OldStatus { get; set; }
+ public string Message { get; set; }
+ public Exception Exception { get; set; }
+ public DateTime Timestamp { get; private set; }
+
+ public ConnectionStatusChangedEventArgs(ConnectionStatus oldStatus, ConnectionStatus newStatus, string message = null, Exception exception = null)
+ {
+ OldStatus = oldStatus;
+ NewStatus = newStatus;
+ Message = message;
+ Exception = exception;
+ Timestamp = DateTime.Now;
+ }
+ }
+
+ ///
+ /// 连接异常事件参数
+ ///
+ public class ConnectionExceptionEventArgs : EventArgs
+ {
+ public Exception Exception { get; private set; }
+ public string ContextMessage { get; private set; }
+ public DateTime Timestamp { get; private set; }
+
+ public ConnectionExceptionEventArgs(Exception exception, string contextMessage)
+ {
+ Exception = exception;
+ ContextMessage = contextMessage;
+ Timestamp = DateTime.Now;
+ }
+ }
+
+ ///
+ /// 图像数据接收事件参数
+ ///
+ public class ImageReceivedEventArgs : EventArgs
+ {
+ public Image ImageData { get; private set; }
+ public DateTime Timestamp { get; private set; }
+
+ public ImageReceivedEventArgs(Image imageData)
+ {
+ ImageData = imageData;
+ Timestamp = DateTime.Now;
+ }
+ }
+
+ ///
+ /// 设备管理类,负责设备搜索、连接和断开(纯C#实现)
+ ///
+ public class DeviceManager : IDisposable
+ {
+ #region 私有成员
+
+ // 设备IP地址
+ private string _deviceIp = "192.168.100.2"; // 默认IP
+ private int _devicePort = 8080; // 默认端口
+
+ // 设备ID列表
+ private List _deviceIds;
+
+ // 当前连接的设备ID
+ private int _currentDeviceId = -1;
+
+ // 是否已初始化
+ private bool _isInitialized;
+
+ // 连接状态
+ private ConnectionStatus _connectionStatus = ConnectionStatus.Disconnected;
+
+ // 图像模式
+ private ImageMode _currentImageMode = ImageMode.Thermal;
+
+ // 自动重连相关
+ private bool _isAutoReconnectEnabled = false;
+ private int _reconnectInterval = 5000; // 默认5秒
+ private int _maxReconnectAttempts = 5;
+ private int _reconnectAttempts = 0;
+ private System.Threading.Timer _reconnectTimer;
+ private ManualResetEventSlim _stopRequested = new ManualResetEventSlim(false);
+
+ // 心跳检测相关
+ private System.Threading.Timer _heartbeatTimer;
+ private int _heartbeatInterval = 30000; // 30秒心跳
+
+ #endregion 私有成员
+
+ #region 私有方法
+
+ ///
+ /// 从设备字符串中解析设备ID
+ ///
+ /// 设备字符串
+ /// 设备ID
+ private int ParseDeviceId(string deviceString)
+ {
+ try
+ {
+ // 简单的ID解析逻辑,实际应该根据设备返回的数据格式来解析
+ if (int.TryParse(deviceString.Trim(), out int id))
+ {
+ return id;
+ }
+ return -1;
+ }
+ catch (Exception)
+ {
+ return -1;
+ }
+ }
+
+ ///
+ /// 更新连接状态
+ ///
+ /// 新的连接状态
+ /// 状态变更消息
+ /// 相关异常(如果有)
+ private void UpdateConnectionStatus(ConnectionStatus newStatus, string message = null, Exception exception = null)
+ {
+ if (_connectionStatus != newStatus)
+ {
+ ConnectionStatus oldStatus = _connectionStatus;
+ _connectionStatus = newStatus;
+ OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(oldStatus, newStatus, message, exception));
+ }
+ }
+
+ ///
+ /// 初始化设备管理器
+ ///
+ /// 是否初始化成功
+ private bool Initialize()
+ {
+ try
+ {
+ _deviceIds = new List();
+ _reconnectTimer = null;
+ _heartbeatTimer = null;
+ _isInitialized = true;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _isInitialized = false;
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
+ return false;
+ }
+ }
+
+ ///
+ /// 检查IP是否可达
+ ///
+ /// IP地址
+ /// 是否可达
+ private bool IsIpReachable(string ipAddress)
+ {
+ try
+ {
+ using (Ping ping = new Ping())
+ {
+ PingReply reply = ping.Send(ipAddress, 1000); // 1秒超时
+ return reply.Status == IPStatus.Success;
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 停止所有图像接收线程
+ ///
+ private void StopAllImageReceivers()
+ {
+ _stopRequested.Set();
+ // 给线程一点时间来结束
+ Thread.Sleep(100);
+ }
+
+ ///
+ /// 重置停止标志
+ ///
+ private void ResetStopFlag()
+ {
+ _stopRequested.Reset();
+ }
+
+ #endregion 私有方法
+
+ #region 公共事件
+
+ ///
+ /// 连接状态变更事件
+ ///
+ public event EventHandler ConnectionStatusChanged;
+
+ ///
+ /// 连接异常事件
+ ///
+ public event EventHandler ConnectionException;
+
+ ///
+ /// 图像数据接收事件
+ ///
+ public event EventHandler ImageReceived;
+
+ #endregion 公共事件
+
+ ///
+ /// 触发连接状态变更事件
+ ///
+ /// 事件参数
+ protected virtual void OnConnectionStatusChanged(ConnectionStatusChangedEventArgs e)
+ {
+ ConnectionStatusChanged?.Invoke(this, e);
+ }
+
+ ///
+ /// 触发连接异常事件
+ ///
+ /// 事件参数
+ protected virtual void OnConnectionException(ConnectionExceptionEventArgs e)
+ {
+ ConnectionException?.Invoke(this, e);
+ }
+
+ ///
+ /// 触发图像接收事件
+ ///
+ /// 事件参数
+ protected virtual void OnImageReceived(ImageReceivedEventArgs e)
+ {
+ ImageReceived?.Invoke(this, e);
+ }
+
+ #region 公共属性
+
+ ///
+ /// 当前图像模式
+ ///
+ public ImageMode CurrentImageMode
+ {
+ get { return _currentImageMode; }
+ set
+ {
+ if (_currentImageMode != value)
+ {
+ _currentImageMode = value;
+ // 如果已连接,发送模式变更命令
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ SendModeChangeCommand(value);
+ }
+ }
+ }
+ }
+
+ ///
+ /// 连接状态
+ ///
+ public ConnectionStatus ConnectionStatus
+ {
+ get { return _connectionStatus; }
+ }
+
+ ///
+ /// 是否启用自动重连
+ ///
+ public bool AutoReconnectEnabled
+ {
+ get { return _isAutoReconnectEnabled; }
+ set
+ {
+ _isAutoReconnectEnabled = value;
+ if (value && _connectionStatus == ConnectionStatus.Disconnected)
+ {
+ StartAutoReconnect();
+ }
+ else if (!value)
+ {
+ StopAutoReconnect();
+ }
+ }
+ }
+
+ ///
+ /// 重连间隔(毫秒)
+ ///
+ public int ReconnectInterval
+ {
+ get { return _reconnectInterval; }
+ set
+ {
+ if (value > 0)
+ {
+ _reconnectInterval = value;
+ // 如果已经在自动重连中,更新定时器
+ if (_isAutoReconnectEnabled && _reconnectTimer != null)
+ {
+ _reconnectTimer.Change(0, _reconnectInterval);
+ }
+ }
+ }
+ }
+
+ ///
+ /// 最大重连次数
+ ///
+ public int MaxReconnectAttempts
+ {
+ get { return _maxReconnectAttempts; }
+ set
+ {
+ if (value >= 0)
+ {
+ _maxReconnectAttempts = value;
+ }
+ }
+ }
+
+ ///
+ /// 设备IP地址
+ ///
+ public string DeviceIp
+ {
+ get { return _deviceIp; }
+ set
+ {
+ if (_deviceIp != value)
+ {
+ _deviceIp = value;
+ // 如果已连接,重新连接
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ Disconnect();
+ ConnectDevice();
+ }
+ }
+ }
+ }
+
+ ///
+ /// 设备端口
+ ///
+ public int DevicePort
+ {
+ get { return _devicePort; }
+ set
+ {
+ if (_devicePort != value && value > 0 && value <= 65535)
+ {
+ _devicePort = value;
+ // 如果已连接,重新连接
+ if (_connectionStatus == ConnectionStatus.Connected)
+ {
+ Disconnect();
+ ConnectDevice();
+ }
+ }
+ }
+ }
+
+ ///
+ /// 心跳间隔(毫秒)
+ ///
+ public int HeartbeatInterval
+ {
+ get { return _heartbeatInterval; }
+ set
+ {
+ if (value > 0)
+ {
+ _heartbeatInterval = value;
+ // 如果已经启用心跳检测,更新定时器
+ if (_heartbeatTimer != null)
+ {
+ _heartbeatTimer.Change(0, _heartbeatInterval);
+ }
+ }
+ }
+ }
+
+ #endregion 公共属性
+
+ #region 视频流处理方法
+
+ ///
+ /// 设置视频模式
+ ///
+ /// 图像模式
+ public void SetImageMode(ImageMode mode)
+ {
+ CurrentImageMode = mode;
+ }
+
+ ///
+ /// 发送模式变更命令
+ ///
+ /// 图像模式
+ private void SendModeChangeCommand(ImageMode mode)
+ {
+ try
+ {
+ // 使用HTTP请求来切换图像模式
+ string modePath = mode == ImageMode.Thermal ? "thermal.jpg" : "color.jpg";
+ string url = $"http://{_deviceIp}:{_devicePort}/{modePath}?mode={mode}";
+
+ using (WebClient client = new WebClient())
+ {
+ client.DownloadData(url); // 发送请求但不处理响应
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"切换图像模式失败: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "发送模式变更命令失败"));
+ }
+ }
+
+ ///
+ /// 开始接收图像
+ ///
+ public void StartReceiveImage()
+ {
+ if (_connectionStatus != ConnectionStatus.Connected)
+ {
+ throw new InvalidOperationException("设备未连接,无法开始接收图像");
+ }
+
+ ResetStopFlag();
+ Thread imageThread = new Thread(ReceiveImageThread)
+ {
+ IsBackground = true,
+ Name = "ImageReceiver"
+ };
+ imageThread.Start();
+ }
+
+ ///
+ /// 停止接收图像
+ ///
+ public void StopReceiveImage()
+ {
+ StopAllImageReceivers();
+ }
+
+ ///
+ /// 图像接收线程
+ ///
+ private void ReceiveImageThread()
+ {
+ try
+ {
+ while (!_stopRequested.IsSet)
+ {
+ try
+ {
+ // 根据当前模式选择图像路径
+ string imagePath = _currentImageMode == ImageMode.Thermal ? "thermal.jpg" : "color.jpg";
+ string url = $"http://{_deviceIp}:{_devicePort}/{imagePath}?{DateTime.Now.Ticks}";
+
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+ request.Timeout = 5000; // 5秒超时
+ request.Method = "GET";
+ request.KeepAlive = true;
+
+ using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
+ {
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ using (Stream stream = response.GetResponseStream())
+ {
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ memoryStream.Write(buffer, 0, bytesRead);
+ }
+
+ byte[] imageData = memoryStream.ToArray();
+ if (IsValidImageData(imageData))
+ {
+ using (MemoryStream imageStream = new MemoryStream(imageData))
+ {
+ Image image = Image.FromStream(imageStream);
+ OnImageReceived(new ImageReceivedEventArgs(image));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (WebException webEx)
+ {
+ // 处理超时等网络错误
+ if (webEx.Status == WebExceptionStatus.Timeout)
+ {
+ Console.WriteLine("图像接收超时");
+ }
+ else if (webEx.Status == WebExceptionStatus.ConnectFailure)
+ {
+ Console.WriteLine("图像连接失败,可能设备已断开");
+ // 触发连接断开事件
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "图像连接失败,设备可能已断开", webEx);
+ // 如果启用了自动重连,开始重连
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ break; // 退出图像接收线程
+ }
+ else
+ {
+ Console.WriteLine($"图像接收错误: {webEx.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(webEx, "图像接收过程中发生异常"));
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"图像处理错误: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "图像处理过程中发生异常"));
+ }
+
+ // 短暂休眠,控制帧率
+ Thread.Sleep(100);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"图像接收线程异常: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "图像接收线程异常"));
+ }
+ }
+
+ ///
+ /// 验证图像数据是否有效
+ ///
+ /// 图像数据
+ /// 是否为有效图像数据
+ private bool IsValidImageData(byte[] data)
+ {
+ if (data == null || data.Length < 8)
+ {
+ return false;
+ }
+
+ // 检查JPEG文件头(FF D8)
+ if (data[0] == 0xFF && data[1] == 0xD8)
+ {
+ return true;
+ }
+
+ // 检查PNG文件头
+ byte[] pngHeader = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
+ if (data.Length >= pngHeader.Length)
+ {
+ bool isPng = true;
+ for (int i = 0; i < pngHeader.Length; i++)
+ {
+ if (data[i] != pngHeader[i])
+ {
+ isPng = false;
+ break;
+ }
+ }
+ if (isPng)
+ {
+ return true;
+ }
+ }
+
+ // 检查BMP文件头
+ if (data[0] == 0x42 && data[1] == 0x4D) // "BM"
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion 视频流处理方法
+
+ #region 公共方法
+
+ ///
+ /// 搜索设备(同步版本)
+ ///
+ public List SearchDevices()
+ {
+ try
+ {
+ if (!_isInitialized && !Initialize())
+ {
+ return new List();
+ }
+
+ List foundDevices = new List();
+ _deviceIds.Clear();
+
+ // 1. 尝试UDP广播搜索
+ List discoveredIps = BroadcastSearch();
+
+ // 2. 如果UDP广播没有找到设备,尝试在本地网络扫描
+ if (discoveredIps.Count == 0)
+ {
+ discoveredIps = ScanLocalNetwork();
+ }
+
+ // 为每个发现的IP分配一个设备ID
+ int deviceId = 1;
+ foreach (string ip in discoveredIps)
+ {
+ _deviceIds.Add(deviceId);
+ foundDevices.Add(deviceId);
+ deviceId++;
+ }
+
+ return foundDevices;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"搜索设备失败: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "搜索设备失败"));
+ return new List();
+ }
+ }
+
+ ///
+ /// 搜索设备(异步版本)
+ ///
+ public Task> SearchDevicesAsync()
+ {
+ return Task.Factory.StartNew(() => SearchDevices());
+ }
+
+ ///
+ /// UDP广播搜索设备
+ ///
+ /// 发现的设备IP列表
+ private List BroadcastSearch()
+ {
+ List discoveredIps = new List();
+
+ try
+ {
+ // 使用UDP广播查找设备
+ using (UdpClient udpClient = new UdpClient())
+ {
+ udpClient.EnableBroadcast = true;
+ udpClient.Client.ReceiveTimeout = 2000;
+
+ // 广播地址和端口
+ IPEndPoint broadcastEndpoint = new IPEndPoint(IPAddress.Broadcast, 8080);
+
+ // 发送搜索命令
+ byte[] searchCommand = Encoding.ASCII.GetBytes("SEARCH_DEVICE");
+ udpClient.Send(searchCommand, searchCommand.Length, broadcastEndpoint);
+
+ // 接收响应
+ IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0);
+
+ try
+ {
+ byte[] response = udpClient.Receive(ref remoteEndpoint);
+ string responseString = Encoding.ASCII.GetString(response);
+
+ // 检查响应是否来自我们的设备
+ if (responseString.Contains("DEVICE_FOUND"))
+ {
+ discoveredIps.Add(remoteEndpoint.Address.ToString());
+ }
+ }
+ catch (SocketException ex)
+ {
+ // 超时异常,正常处理
+ if (ex.SocketErrorCode != SocketError.TimedOut)
+ {
+ Console.WriteLine($"UDP接收异常: {ex.Message}");
+ }
+ }
+ }
+
+ // 额外检查默认IP是否可达
+ if (IsIpReachable(_deviceIp))
+ {
+ if (!discoveredIps.Contains(_deviceIp))
+ {
+ discoveredIps.Add(_deviceIp);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"UDP广播搜索失败: {ex.Message}");
+ }
+
+ return discoveredIps;
+ }
+
+ ///
+ /// 扫描本地网络
+ ///
+ /// 发现的设备IP列表
+ private List ScanLocalNetwork()
+ {
+ List discoveredIps = new List();
+
+ try
+ {
+ // 获取本地IP地址和子网
+ foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
+ {
+ if (ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback)
+ {
+ foreach (UnicastIPAddressInformation ipInfo in ni.GetIPProperties().UnicastAddresses)
+ {
+ if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ IPAddress ip = ipInfo.Address;
+ IPAddress subnet = GetSubnetMask(ipInfo.IPv4Mask);
+
+ // 扫描常见的设备IP范围
+ // 这里简化处理,只扫描几个常用的IP
+ string[] commonDeviceIps = new string[]
+ {
+ "192.168.100.2", // 默认IP
+ "192.168.1.10",
+ "192.168.0.10",
+ "10.0.0.10"
+ };
+
+ foreach (string deviceIp in commonDeviceIps)
+ {
+ if (IsIpReachable(deviceIp))
+ {
+ if (!discoveredIps.Contains(deviceIp))
+ {
+ discoveredIps.Add(deviceIp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"本地网络扫描失败: {ex.Message}");
+ }
+
+ return discoveredIps;
+ }
+
+ ///
+ /// 获取子网掩码
+ ///
+ /// IP掩码
+ /// 子网掩码
+ private IPAddress GetSubnetMask(IPAddress mask)
+ {
+ return mask;
+ }
+
+ ///
+ /// 连接设备
+ ///
+ /// 是否连接成功
+ public bool ConnectDevice()
+ {
+ try
+ {
+ UpdateConnectionStatus(ConnectionStatus.Connecting, "开始连接设备...");
+
+ // 确保已初始化
+ if (!_isInitialized && !Initialize())
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备管理器初始化失败");
+ return false;
+ }
+
+ // 停止任何现有的重连定时器
+ StopAutoReconnect();
+
+ // 停止任何正在进行的连接检查
+ StopAllImageReceivers();
+
+ // 检查IP是否可达
+ if (!IsIpReachable(_deviceIp))
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, $"设备IP {_deviceIp} 不可达");
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ return false;
+ }
+
+ // 尝试TCP连接测试
+ bool tcpConnected = TestTcpConnection(_deviceIp, _devicePort);
+ if (!tcpConnected)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, $"TCP连接到 {_deviceIp}:{_devicePort} 失败");
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ return false;
+ }
+
+ // 发送连接命令
+ bool commandSent = SendConnectCommand();
+ if (!commandSent)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "发送连接命令失败");
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ return false;
+ }
+
+ // 启动心跳检测
+ StartHeartbeat();
+
+ // 设置连接状态
+ _currentDeviceId = 1; // 简化处理,实际应该根据设备返回的数据设置
+ UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接成功");
+
+ // 重置重连尝试计数
+ _reconnectAttempts = 0;
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"连接设备失败: {ex.Message}");
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "连接设备时发生异常", ex);
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "连接设备失败"));
+
+ // 如果启用了自动重连,开始重连
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+
+ return false;
+ }
+ }
+
+ ///
+ /// 测试TCP连接
+ ///
+ /// IP地址
+ /// 端口
+ /// 是否连接成功
+ private bool TestTcpConnection(string ip, int port)
+ {
+ try
+ {
+ using (TcpClient client = new TcpClient())
+ {
+ IAsyncResult result = client.BeginConnect(ip, port, null, null);
+ bool success = result.AsyncWaitHandle.WaitOne(2000); // 2秒超时
+ if (success)
+ {
+ client.EndConnect(result);
+ return true;
+ }
+ else
+ {
+ client.Close();
+ return false;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 发送连接命令
+ ///
+ /// 是否发送成功
+ private bool SendConnectCommand()
+ {
+ try
+ {
+ // 使用HTTP GET请求作为连接命令
+ string url = $"http://{_deviceIp}:{_devicePort}/connect";
+ WebRequest request = WebRequest.Create(url);
+ request.Timeout = 3000; // 3秒超时
+ using (WebResponse response = request.GetResponse())
+ {
+ using (Stream dataStream = response.GetResponseStream())
+ {
+ using (StreamReader reader = new StreamReader(dataStream, Encoding.ASCII))
+ {
+ string responseString = reader.ReadToEnd();
+ return responseString.Contains("CONNECTED");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"发送连接命令失败: {ex.Message}");
+ // 即使连接命令失败,我们也继续,因为有些设备可能不需要额外的连接命令
+ return true;
+ }
+ }
+
+ ///
+ /// 断开连接
+ ///
+ public void Disconnect()
+ {
+ try
+ {
+ // 停止重连定时器
+ StopAutoReconnect();
+
+ // 停止心跳检测
+ StopHeartbeat();
+
+ // 停止图像接收
+ StopAllImageReceivers();
+
+ // 设置断开状态
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "设备已断开连接");
+
+ // 重置设备ID
+ _currentDeviceId = -1;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"断开连接时发生异常: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "断开连接失败"));
+ }
+ }
+
+ ///
+ /// 开始自动重连
+ ///
+ private void StartAutoReconnect()
+ {
+ // 停止现有的重连定时器
+ StopAutoReconnect();
+
+ // 创建新的重连定时器
+ _reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, _reconnectInterval);
+ }
+
+ ///
+ /// 停止自动重连
+ ///
+ private void StopAutoReconnect()
+ {
+ if (_reconnectTimer != null)
+ {
+ _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ _reconnectTimer.Dispose();
+ _reconnectTimer = null;
+ }
+ }
+
+ ///
+ /// 重连回调
+ ///
+ /// 定时器状态
+ private void ReconnectCallback(object state)
+ {
+ try
+ {
+ // 检查是否达到最大重连次数
+ if (_maxReconnectAttempts > 0 && _reconnectAttempts >= _maxReconnectAttempts)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, $"达到最大重连次数 {_maxReconnectAttempts}");
+ StopAutoReconnect();
+ return;
+ }
+
+ _reconnectAttempts++;
+ UpdateConnectionStatus(ConnectionStatus.Reconnecting, $"尝试重连... (第 {_reconnectAttempts} 次)");
+
+ // 检查IP是否可达
+ if (IsIpReachable(_deviceIp))
+ {
+ // 尝试连接
+ bool connected = ConnectDevice();
+ if (connected)
+ {
+ UpdateConnectionStatus(ConnectionStatus.Connected, "自动重连成功");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"自动重连时发生异常: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "自动重连失败"));
+ }
+ }
+
+ ///
+ /// 开始心跳检测
+ ///
+ private void StartHeartbeat()
+ {
+ // 停止现有的心跳定时器
+ StopHeartbeat();
+
+ // 创建新的心跳定时器
+ _heartbeatTimer = new System.Threading.Timer(HeartbeatCallback, null, 0, _heartbeatInterval);
+ }
+
+ ///
+ /// 停止心跳检测
+ ///
+ private void StopHeartbeat()
+ {
+ if (_heartbeatTimer != null)
+ {
+ _heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ _heartbeatTimer.Dispose();
+ _heartbeatTimer = null;
+ }
+ }
+
+ ///
+ /// 心跳检测回调
+ ///
+ /// 定时器状态
+ private void HeartbeatCallback(object state)
+ {
+ try
+ {
+ // 检查设备是否可达
+ if (!IsIpReachable(_deviceIp))
+ {
+ Console.WriteLine("心跳检测失败,设备不可达");
+ UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达");
+
+ // 如果启用了自动重连,开始重连
+ if (_isAutoReconnectEnabled)
+ {
+ StartAutoReconnect();
+ }
+ return;
+ }
+
+ // 尝试发送心跳请求
+ string url = $"http://{_deviceIp}:{_devicePort}/heartbeat";
+ try
+ {
+ WebRequest request = WebRequest.Create(url);
+ request.Timeout = 2000; // 2秒超时
+ using (WebResponse response = request.GetResponse())
+ {
+ using (Stream dataStream = response.GetResponseStream())
+ {
+ // 只需确保请求成功,不需要读取内容
+ dataStream.ReadByte();
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // 心跳请求失败,但继续使用PING作为备用检测
+ Console.WriteLine("心跳请求失败,但PING检测通过");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"心跳检测时发生异常: {ex.Message}");
+ OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败"));
+ }
+ }
+
+ #endregion 公共方法
+
+ #region IDisposable 实现
+
+ private bool _disposed = false;
+
+ ///
+ /// 释放资源
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// 释放资源
+ ///
+ /// 是否释放托管资源
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ {
+ // 释放托管资源
+ StopAutoReconnect();
+ StopHeartbeat();
+ StopAllImageReceivers();
+ _stopRequested.Dispose();
+ }
+
+ // 释放非托管资源
+ _isInitialized = false;
+ _currentDeviceId = -1;
+
+ _disposed = true;
+ }
+ }
+
+ ///
+ /// 析构函数
+ ///
+ ~DeviceManager()
+ {
+ Dispose(false);
+ }
+
+ #endregion IDisposable 实现
+
+ #region 辅助方法
+
+ ///
+ /// 检查是否为HTTP响应
+ ///
+ /// 数据
+ /// 是否为HTTP响应
+ private bool IsHttpResponse(byte[] data)
+ {
+ if (data == null || data.Length < 10)
+ {
+ return false;
+ }
+
+ // 检查是否以HTTP开头
+ string startText = Encoding.ASCII.GetString(data, 0, Math.Min(10, data.Length));
+ return startText.StartsWith("HTTP/");
+ }
+
+ ///
+ /// 查找图像数据的起始位置
+ ///
+ /// 数据
+ /// 图像数据起始位置
+ private int FindImageStartPosition(byte[] data)
+ {
+ if (data == null || data.Length < 2)
+ {
+ return -1;
+ }
+
+ // 查找JPEG起始标记 (FF D8)
+ for (int i = 0; i < data.Length - 1; i++)
+ {
+ if (data[i] == 0xFF && data[i + 1] == 0xD8)
+ {
+ return i;
+ }
+ }
+
+ // 查找PNG起始标记
+ byte[] pngHeader = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
+ if (data.Length >= pngHeader.Length)
+ {
+ for (int i = 0; i <= data.Length - pngHeader.Length; i++)
+ {
+ bool isPngHeader = true;
+ for (int j = 0; j < pngHeader.Length; j++)
+ {
+ if (data[i + j] != pngHeader[j])
+ {
+ isPngHeader = false;
+ break;
+ }
+ }
+ if (isPngHeader)
+ {
+ return i;
+ }
+ }
+ }
+
+ // 查找BMP起始标记 (BM)
+ for (int i = 0; i < data.Length - 1; i++)
+ {
+ if (data[i] == 0x42 && data[i + 1] == 0x4D)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ ///
+ /// 查找图像数据的结束位置
+ ///
+ /// 数据
+ /// 起始位置
+ /// 图像数据结束位置
+ private int FindImageEndPosition(byte[] data, int startPos)
+ {
+ if (data == null || startPos < 0 || startPos >= data.Length - 1)
+ {
+ return -1;
+ }
+
+ // 检查是否是JPEG格式
+ if (data[startPos] == 0xFF && data[startPos + 1] == 0xD8)
+ {
+ // 查找JPEG结束标记 (FF D9)
+ for (int i = startPos + 2; i < data.Length - 1; i++)
+ {
+ if (data[i] == 0xFF && data[i + 1] == 0xD9)
+ {
+ return i + 2; // 包含结束标记
+ }
+ }
+ }
+
+ // 对于PNG和BMP,我们假设数据是完整的
+ return data.Length;
+ }
+
+ ///
+ /// 计算图像数据长度
+ ///
+ /// 图像数据
+ /// 图像数据长度
+ private int CalculateImageLength(byte[] data)
+ {
+ if (data == null)
+ {
+ return 0;
+ }
+
+ // 简化实现:如果是JPEG,查找结束标记
+ for (int i = 0; i < data.Length - 1; i++)
+ {
+ if (data[i] == 0xFF && data[i + 1] == 0xD9)
+ {
+ return i + 2;
+ }
+ }
+
+ // PNG和BMP通常会有文件长度信息,这里简化处理
+ // 对于完整的实现,应该解析文件头中的长度信息
+ return data.Length;
+ }
+
+ #endregion 辅助方法
+ }
+}
\ No newline at end of file
diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
index d6f940a..173d850 100644
--- a/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
+++ b/Windows/CS/Framework4.0/Toprie/Toprie/Toprie.csproj
@@ -66,6 +66,7 @@
Camera.cs
+