From 51b39cee077f795b714624ea00edb0d472b90bb5 Mon Sep 17 00:00:00 2001 From: alivender <13898766233@163.com> Date: Mon, 4 Aug 2025 11:54:58 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E6=B7=BB=E5=8A=A0=E4=BA=86HDMI=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=B5=81Client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/Peripherals/CameraClient.cs | 2 + server/src/Peripherals/HdmiInClient.cs | 118 ++++++++++++++++++ server/src/Peripherals/LogicAnalyzerClient.cs | 2 + server/src/Peripherals/OscilloscopeClient.cs | 2 + server/src/UdpClientPool.cs | 5 +- 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 server/src/Peripherals/HdmiInClient.cs diff --git a/server/src/Peripherals/CameraClient.cs b/server/src/Peripherals/CameraClient.cs index 708caae..c201318 100644 --- a/server/src/Peripherals/CameraClient.cs +++ b/server/src/Peripherals/CameraClient.cs @@ -1,6 +1,7 @@ using System.Net; using DotNext; using Peripherals.PowerClient; +using WebProtocol; namespace Peripherals.CameraClient; @@ -225,6 +226,7 @@ class Camera this.taskID, // taskID FrameAddr, (int)_currentFrameLength, // 使用当前分辨率的动态大小 + BurstType.ExtendBurst, this.timeout); if (!result.IsSuccessful) diff --git a/server/src/Peripherals/HdmiInClient.cs b/server/src/Peripherals/HdmiInClient.cs new file mode 100644 index 0000000..4fd6e35 --- /dev/null +++ b/server/src/Peripherals/HdmiInClient.cs @@ -0,0 +1,118 @@ +using System.Net; +using DotNext; +using Peripherals.PowerClient; +using WebProtocol; + +namespace Peripherals.HdmiInClient; + +static class HdmiInAddr +{ + public const UInt32 BASE = 0xA000_0000; + public const UInt32 HdmiIn_CTRL = BASE + 0x0; //[0]: rstn, 0 is reset. + public const UInt32 HdmiIn_READFIFO = BASE + 0x1; +} + +class HdmiIn +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout = 500; + readonly int taskID; + readonly int port; + readonly string address; + private IPEndPoint ep; + + // 动态分辨率参数 + private UInt16 _currentWidth = 960; + private UInt16 _currentHeight = 540; + private UInt32 _currentFrameLength = 960 * 540 * 2 / 4; // RGB565格式,2字节/像素,按4字节对齐 + + /// + /// 初始化HDMI输入客户端 + /// + /// HDMI输入设备IP地址 + /// HDMI输入设备端口 + /// 超时时间(毫秒) + public HdmiIn(string address, int port, int timeout = 500) + { + if (timeout < 0) + throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); + this.address = address; + this.port = port; + this.ep = new IPEndPoint(IPAddress.Parse(address), port); + this.timeout = timeout; + } + + public async ValueTask> EnableTrans(bool isEnable) + { + var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, HdmiInAddr.HdmiIn_CTRL, (isEnable ? 0x00000001u : 0x00000000u)); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to write HdmiIn_CTRL to HdmiIn at {this.address}:{this.port}, error: {ret.Error}"); + return new(ret.Error); + } + if (!ret.Value) + { + logger.Error($"HdmiIn_CTRL write returned false for HdmiIn at {this.address}:{this.port}"); + return false; + } + return true; + } + + /// + /// 读取一帧图像数据 + /// + /// 包含图像数据的字节数组 + public async ValueTask> ReadFrame() + { + // 只在第一次或出错时清除UDP缓冲区,避免每帧都清除造成延迟 + // MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + logger.Trace($"Reading frame from HdmiIn {this.address}"); + + // 使用UDPClientPool读取图像帧数据 + var result = await UDPClientPool.ReadAddr4BytesAsync( + this.ep, + this.taskID, // taskID + HdmiInAddr.HdmiIn_READFIFO, + (int)_currentFrameLength, // 使用当前分辨率的动态大小 + BurstType.FixedBurst, + this.timeout); + + if (!result.IsSuccessful) + { + logger.Error($"Failed to read frame from HdmiIn {this.address}:{this.port}, error: {result.Error}"); + // 读取失败时清除缓冲区,为下次读取做准备 + try + { + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + } + catch (Exception ex) + { + logger.Warn($"Failed to clear UDP data after read error: {ex.Message}"); + } + return new(result.Error); + } + + logger.Trace($"Successfully read frame from HdmiIn {this.address}:{this.port}, data length: {result.Value.Length} bytes"); + return result.Value; + } + + /// + /// 获取当前分辨率 + /// + /// 当前分辨率(宽度, 高度) + public (int Width, int Height) GetCurrentResolution() + { + return (_currentWidth, _currentHeight); + } + + /// + /// 获取当前帧长度 + /// + /// 当前帧长度 + public UInt32 GetCurrentFrameLength() + { + return _currentFrameLength; + } +} diff --git a/server/src/Peripherals/LogicAnalyzerClient.cs b/server/src/Peripherals/LogicAnalyzerClient.cs index 2913fd5..5bb1693 100644 --- a/server/src/Peripherals/LogicAnalyzerClient.cs +++ b/server/src/Peripherals/LogicAnalyzerClient.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Net; using Common; using DotNext; +using WebProtocol; namespace Peripherals.LogicAnalyzerClient; @@ -475,6 +476,7 @@ public class Analyzer this.taskID, AnalyzerAddr.STORE_OFFSET_ADDR, capture_length, + BurstType.ExtendBurst, // 使用扩展突发读取 this.timeout ); if (!ret.IsSuccessful) diff --git a/server/src/Peripherals/OscilloscopeClient.cs b/server/src/Peripherals/OscilloscopeClient.cs index b4fabe5..173924e 100644 --- a/server/src/Peripherals/OscilloscopeClient.cs +++ b/server/src/Peripherals/OscilloscopeClient.cs @@ -1,6 +1,7 @@ using System.Net; using Common; using DotNext; +using WebProtocol; namespace Peripherals.OscilloscopeClient; @@ -319,6 +320,7 @@ class Oscilloscope this.taskID, OscilloscopeAddr.RD_DATA_ADDR, (int)OscilloscopeAddr.RD_DATA_LENGTH / 32, + BurstType.ExtendBurst, // 使用扩展突发读取 this.timeout ); if (!ret.IsSuccessful) diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs index b2cd059..4222c9f 100644 --- a/server/src/UdpClientPool.cs +++ b/server/src/UdpClientPool.cs @@ -433,11 +433,12 @@ public class UDPClientPool /// IP端点(IP地址与端口) /// 任务ID /// 设备地址 + /// 突发类型 /// 要读取的数据长度(4字节) /// 超时时间(毫秒) /// 读取结果,包含接收到的字节数组 public static async ValueTask> ReadAddr4BytesAsync( - IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, int timeout = 1000) + IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, BurstType burstType, int timeout = 1000) { var pkgList = new List(); var resultData = new List(); @@ -460,7 +461,7 @@ public class UDPClientPool var opts = new SendAddrPackOptions { - BurstType = BurstType.FixedBurst, + BurstType = burstType, CommandID = Convert.ToByte(taskID), IsWrite = false, BurstLength = (byte)(currentSegmentSize - 1),