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),