From 8c404d407213ef9aee9596a8763abcb749ec8085 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Thu, 31 Jul 2025 14:30:30 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DDebugger=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=95=B0=E6=8D=AE=E6=97=B6=EF=BC=8C=E6=9C=80=E7=BB=88?= =?UTF-8?q?=E8=BD=AC=E5=8C=96=E4=B8=BA=E5=AD=97=E8=8A=82=E6=97=B6=E5=87=BA?= =?UTF-8?q?=E7=8E=B0=E7=9A=84=E8=BD=AC=E5=8C=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/Controllers/DebuggerController.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/src/Controllers/DebuggerController.cs b/server/src/Controllers/DebuggerController.cs index 0c3507f..916c500 100644 --- a/server/src/Controllers/DebuggerController.cs +++ b/server/src/Controllers/DebuggerController.cs @@ -406,13 +406,15 @@ public class DebuggerController : ControllerBase return StatusCode(StatusCodes.Status500InternalServerError, "数据越界"); } var sampleBytes = rawData[sampleOffset..(sampleOffset + 4)]; - UInt32 sample = BitConverter.ToUInt32(Common.Number.ReverseBytes(sampleBytes, 4).Value, 0); + UInt32 sample = Common.Number.BytesToUInt32(sampleBytes, true).Value; // 提取wireWidth位 UInt32 mask = (wireWidth == 32) ? 0xFFFFFFFF : ((1u << wireWidth) - 1u); channelUintArr[i] = (sample >> wireStart) & mask; } - logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelUintArr.SelectMany(BitConverter.GetBytes).ToArray())}"); - var base64 = Convert.ToBase64String(channelUintArr.SelectMany(BitConverter.GetBytes).ToArray()); + var channelBytes = new byte[4 * depth]; + Buffer.BlockCopy(channelUintArr, 0, channelBytes, 0, channelBytes.Length); + logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelBytes)}"); + var base64 = Convert.ToBase64String(channelBytes); channelDataList.Add(new ChannelCaptureData { name = channel.name, data = base64 }); } From bafd06162cc3b816f4a7609e17ff11213d59aaf7 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Thu, 31 Jul 2025 15:42:41 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96Common=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BB=A5=E6=8F=90=E9=AB=98=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 - FPGAWebLabServer.sln | 48 ++++ server.test/CommonTest.cs | 61 ---- server.test/NumberTest.cs | 286 +++++++++++++++++++ server.test/UDPServerTest.cs | 138 --------- server/src/Common/Number.cs | 37 +-- server/src/Controllers/DebuggerController.cs | 1 + 7 files changed, 344 insertions(+), 228 deletions(-) create mode 100644 FPGAWebLabServer.sln delete mode 100644 server.test/CommonTest.cs create mode 100644 server.test/NumberTest.cs delete mode 100644 server.test/UDPServerTest.cs diff --git a/.gitignore b/.gitignore index 1d74a0e..6d8d639 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ DebuggerCmd.md *.suo *.ntvs* *.njsproj -*.sln *.sw? *.tsbuildinfo diff --git a/FPGAWebLabServer.sln b/FPGAWebLabServer.sln new file mode 100644 index 0000000..5bf82a5 --- /dev/null +++ b/FPGAWebLabServer.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "server\server.csproj", "{F31D6A0D-0407-41CE-A67E-01B847488EFB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server.test", "server.test\server.test.csproj", "{CC274582-AC3C-4FD1-977C-96F1BC2760BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|x64.ActiveCfg = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|x64.Build.0 = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|x86.ActiveCfg = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Debug|x86.Build.0 = Debug|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|Any CPU.Build.0 = Release|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|x64.ActiveCfg = Release|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|x64.Build.0 = Release|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|x86.ActiveCfg = Release|Any CPU + {F31D6A0D-0407-41CE-A67E-01B847488EFB}.Release|x86.Build.0 = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|x64.Build.0 = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Debug|x86.Build.0 = Debug|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|Any CPU.Build.0 = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|x64.ActiveCfg = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|x64.Build.0 = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|x86.ActiveCfg = Release|Any CPU + {CC274582-AC3C-4FD1-977C-96F1BC2760BD}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/server.test/CommonTest.cs b/server.test/CommonTest.cs deleted file mode 100644 index 1f36b90..0000000 --- a/server.test/CommonTest.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Buffers.Binary; -using Common; - -namespace server.test; - -public class CommonTest -{ - [Fact] - public void ReverseBytesTest() - { - var rnd = new Random(); - var bytesLen = 8; - var bytesArray = new byte[bytesLen]; - rnd.NextBytes(bytesArray); - - var rev2Bytes = new byte[] { - bytesArray[1], - bytesArray[0], - bytesArray[3], - bytesArray[2], - bytesArray[5], - bytesArray[4], - bytesArray[7], - bytesArray[6], - }; - Assert.Equal(Number.ReverseBytes(bytesArray, 2).Value, rev2Bytes); - - var rev4Bytes = new byte[] { - bytesArray[3], - bytesArray[2], - bytesArray[1], - bytesArray[0], - bytesArray[7], - bytesArray[6], - bytesArray[5], - bytesArray[4], - }; - Assert.Equal(Number.ReverseBytes(bytesArray, 4).Value, rev4Bytes); - } - - [Fact] - public void ToBitTest() - { - Assert.Equal(true, Number.ToBit(0xFF, 3).Value); - Assert.Equal(false, Number.ToBit(0x00, 3).Value); - Assert.Equal(true, Number.ToBit(0xAA, 3).Value); - Assert.Equal(false, Number.ToBit(0xAA, 2).Value); - } - - [Fact] - public void ReverseBits() - { - Assert.Equal((byte)0x05, Common.Number.ReverseBits((byte)0xA0)); - - var bytesSrc = new byte[] { 0xAB, 0x00, 0x00, 0x01 }; - var bytes = new byte[4]; - for (int i = 0; i < 4; i++) - bytes[i] = Common.Number.ReverseBits(bytesSrc[i]); - Assert.Equal(new byte[] { 0xD5, 0x00, 0x00, 0x80 }, bytes); - } -} diff --git a/server.test/NumberTest.cs b/server.test/NumberTest.cs new file mode 100644 index 0000000..dee0b42 --- /dev/null +++ b/server.test/NumberTest.cs @@ -0,0 +1,286 @@ +using System.Collections; +using Common; + +namespace CommonTest; + +/// +/// 针对 Common.Number 的单元测试,覆盖所有公开方法 +/// +public class NumberTest +{ + /// + /// 测试 NumberToBytes 的正常与异常情况 + /// + [Fact] + public void Test_NumberToBytes() + { + // 测试大端(isLowNumHigh=false) + var result1 = Number.NumberToBytes(0x12345678ABCDEF01, 8, false); + Assert.True(result1.IsSuccessful); + Assert.Equal(new byte[] { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01 }, result1.Value); + + // 测试小端(isLowNumHigh=true) + var result2 = Number.NumberToBytes(0x12345678ABCDEF01, 8, true); + Assert.True(result2.IsSuccessful); + Assert.Equal(new byte[] { 0x01, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12 }, result2.Value); + + // 测试长度不足(4字节) + var result3 = Number.NumberToBytes(0x12345678, 4, false); + Assert.True(result3.IsSuccessful); + Assert.Equal(new byte[] { 0x12, 0x34, 0x56, 0x78 }, result3.Value); + + // 测试超长 + var result4 = Number.NumberToBytes(0x1, 9, false); + Assert.False(result4.IsSuccessful); + } + + /// + /// 测试 BytesToUInt64 的正常与异常情况 + /// + [Fact] + public void Test_BytesToUInt64() + { + // 正常大端 + var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01 }; + var result = Number.BytesToUInt64((byte[])bytes.Clone(), false); + Assert.True(result.IsSuccessful); + Assert.Equal(0x12345678ABCDEF01UL, result.Value); + + // 正常小端 + var bytes2 = new byte[] { 0x01, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12 }; + var result2 = Number.BytesToUInt64((byte[])bytes2.Clone(), true); + Assert.True(result2.IsSuccessful); + Assert.Equal(0x12345678ABCDEF01UL, result2.Value); + + // 异常:长度超限 + var result3 = Number.BytesToUInt64(new byte[9], false); + Assert.False(result3.IsSuccessful); + + // 异常:不足8字节 + var result4 = Number.BytesToUInt64(new byte[] { 0x01, 0x02 }, false); + Assert.False(result4.IsSuccessful); // BitConverter.ToUInt64 需要8字节 + } + + /// + /// 测试 BytesToUInt32 的正常与异常情况 + /// + [Fact] + public void Test_BytesToUInt32() + { + // 正常大端 + var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78 }; + var result = Number.BytesToUInt32((byte[])bytes.Clone(), false); + Assert.True(result.IsSuccessful); + Assert.Equal(0x12345678U, result.Value); + + // 正常小端 + var bytes2 = new byte[] { 0x78, 0x56, 0x34, 0x12 }; + var result2 = Number.BytesToUInt32((byte[])bytes2.Clone(), true); + Assert.True(result2.IsSuccessful); + Assert.Equal(0x12345678U, result2.Value); + + // 异常:长度超限 + var result3 = Number.BytesToUInt32(new byte[5], false); + Assert.False(result3.IsSuccessful); + + // 异常:不足4字节 + var result4 = Number.BytesToUInt32(new byte[] { 0x01, 0x02 }, false); + Assert.False(result4.IsSuccessful); // BitConverter.ToUInt32 需要4字节 + } + + /// + /// 测试 UInt32ArrayToBytes 的正常与异常情况 + /// + [Fact] + public void Test_UInt32ArrayToBytes() + { + // 正常情况 + var arr = new UInt32[] { 0x12345678, 0xABCDEF01 }; + var result = Number.UInt32ArrayToBytes(arr); + Assert.True(result.IsSuccessful); + // BlockCopy 按小端序 + Assert.Equal(new byte[] { 0x78, 0x56, 0x34, 0x12, 0x01, 0xEF, 0xCD, 0xAB }, result.Value); + + // 空数组 + var result2 = Number.UInt32ArrayToBytes(new UInt32[0]); + Assert.True(result2.IsSuccessful); + Assert.Empty(result2.Value); + } + + /// + /// 测试 MultiBitsToBytes 和 MultiBitsToNumber (ulong) + /// + [Fact] + public void Test_MultiBitsToBytesAndNumber_Ulong() + { + // 合并两个比特段 + var result = Number.MultiBitsToNumber(0b101UL, 3, 0b11UL, 2); + Assert.True(result.IsSuccessful); + Assert.Equal((ulong)0b10111, result.Value); + + // 合并为字节数组 + var bytesResult = Number.MultiBitsToBytes(0b101UL, 3, 0b11UL, 2); + Assert.True(bytesResult.IsSuccessful); + Assert.Equal(new byte[] { 0b10111 }, bytesResult.Value); + + // 超过64位 + var failResult = Number.MultiBitsToNumber(0xFFFFFFFFFFFFFFFF, 64, 1, 1); + Assert.False(failResult.IsSuccessful); + } + + /// + /// 测试 MultiBitsToNumber (uint) + /// + [Fact] + public void Test_MultiBitsToNumber_Uint() + { + var result = Number.MultiBitsToNumber(0b101U, 3, 0b11U, 2); + Assert.True(result.IsSuccessful); + Assert.Equal((uint)0b10111, result.Value); + + // 超过64位 + var failResult = Number.MultiBitsToNumber(uint.MaxValue, 64, 1, 1); + Assert.False(failResult.IsSuccessful); + } + + /// + /// 测试 BitsCheck (ulong) + /// + [Fact] + public void Test_BitsCheck_Ulong() + { + // 完全匹配 + Assert.True(Number.BitsCheck(0b1101UL, 0b1101UL)); + // 不匹配 + Assert.False(Number.BitsCheck(0b1101UL, 0b1001UL)); + // 掩码 + Assert.True(Number.BitsCheck(0b1101UL, 0b1001UL, 0b1001UL)); + } + + /// + /// 测试 BitsCheck (uint) + /// + [Fact] + public void Test_BitsCheck_Uint() + { + Assert.True(Number.BitsCheck(0b1011U, 0b1011U)); + Assert.False(Number.BitsCheck(0b1011U, 0b1001U)); + Assert.True(Number.BitsCheck(0b1011U, 0b1001U, 0b1001U)); + } + + /// + /// 测试 ToBit + /// + [Fact] + public void Test_ToBit() + { + // 取第0位 + var result = Number.ToBit(0b1010U, 0); + Assert.True(result.IsSuccessful); + Assert.False(result.Value); + + // 取第1位 + var result2 = Number.ToBit(0b1010U, 1); + Assert.True(result2.IsSuccessful); + Assert.True(result2.Value); + + // 负数位置 + var result3 = Number.ToBit(0b1010U, -1); + Assert.False(result3.IsSuccessful); + } + + /// + /// 测试 BitsToNumber + /// + [Fact] + public void Test_BitsToNumber() + { + // 5位BitArray + var bits = new BitArray(new bool[] { true, true, false, true, false }); // 0b01011 + var result = Number.BitsToNumber(bits); + Assert.True(result.IsSuccessful); + Assert.Equal((uint)0b01011, result.Value); + + // 超过32位 + var bits2 = new BitArray(33); + Assert.Throws(() => Number.BitsToNumber(bits2)); + } + + /// + /// 测试 StringToBytes + /// + [Fact] + public void Test_StringToBytes() + { + // 16进制字符串 + var bytes = Number.StringToBytes("1234ABCD"); + Assert.Equal(new byte[] { 0x12, 0x34, 0xAB, 0xCD }, bytes); + + // 8位字符串 + var bytes2 = Number.StringToBytes("01020304"); + Assert.Equal(new byte[] { 0x01, 0x02, 0x03, 0x04 }, bytes2); + } + + /// + /// 测试 ReverseBytes + /// + [Fact] + public void Test_ReverseBytes() + { + // 步长为2 + var src = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var result = Number.ReverseBytes(src, 2); + Assert.True(result.IsSuccessful); + Assert.Equal(new byte[] { 0x02, 0x01, 0x04, 0x03 }, result.Value); + + // 步长为4 + var src2 = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var result2 = Number.ReverseBytes(src2, 4); + Assert.True(result2.IsSuccessful); + Assert.Equal(new byte[] { 0x04, 0x03, 0x02, 0x01 }, result2.Value); + + // 步长为1(无变化) + var src3 = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var result3 = Number.ReverseBytes(src3, 1); + Assert.True(result3.IsSuccessful); + Assert.Equal(src3, result3.Value); + + // 步长为0(异常) + var result4 = Number.ReverseBytes(src3, 0); + Assert.False(result4.IsSuccessful); + + // 步长不能整除 + var result5 = Number.ReverseBytes(src3, 3); + Assert.False(result5.IsSuccessful); + } + + /// + /// 测试 ReverseBits (byte) + /// + [Fact] + public void Test_ReverseBits_Byte() + { + // 0b00010010 -> 0b01001000 + byte src = 0b00010010; + byte reversed = Number.ReverseBits(src); + Assert.Equal(0b01001000, reversed); + + // 0b11110000 -> 0b00001111 + Assert.Equal(0b00001111, Number.ReverseBits(0b11110000)); + } + + /// + /// 测试 ReverseBits (byte[]) + /// + [Fact] + public void Test_ReverseBits_ByteArray() + { + var src = new byte[] { 0b00010010, 0b11110000 }; + var reversed = Number.ReverseBits(src); + Assert.Equal(new byte[] { 0b01001000, 0b00001111 }, reversed); + + // 空数组 + var reversed2 = Number.ReverseBits(new byte[0]); + Assert.Empty(reversed2); + } +} diff --git a/server.test/UDPServerTest.cs b/server.test/UDPServerTest.cs deleted file mode 100644 index 3274507..0000000 --- a/server.test/UDPServerTest.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Net; -using System.Text; -using Common; -using Xunit.Abstractions; - -namespace server.test; - - -public class UDPServerTest -{ - const string address = "127.0.0.1"; - const int port = 33000; - private static readonly UDPServer udpServer = new UDPServer(port); - - private readonly ITestOutputHelper output; - - public UDPServerTest(ITestOutputHelper output) - { - this.output = output; - udpServer.Start(); - } - - [Fact] - public void UDPDataDeepClone() - { - var udpData = new UDPData() - { - DateTime = DateTime.Now, - Address = "127.0.0.1", - Port = 1234, - Data = new byte[] { 0xf0, 00, 00, 00 }, - HasRead = false - }; - var cloneUdpData = udpData.DeepClone(); - - Assert.Equal(udpData.DateTime, cloneUdpData.DateTime); - Assert.Equal(udpData.Address, cloneUdpData.Address); - Assert.Equal(udpData.Port, cloneUdpData.Port); - Assert.Equal(udpData.Data, cloneUdpData.Data); - Assert.Equal(udpData.HasRead, cloneUdpData.HasRead); - - udpData.DateTime = DateTime.Now; - udpData.Address = "192.168.1.1"; - udpData.Port = 33000; - udpData.Data = new byte[] { 0xFF, 00, 00, 00 }; - udpData.HasRead = true; - - Assert.NotNull(cloneUdpData.DateTime); - Assert.NotNull(cloneUdpData.Address); - Assert.NotNull(cloneUdpData.Port); - Assert.NotNull(cloneUdpData.Data); - Assert.NotNull(cloneUdpData.HasRead); - - Assert.NotEqual(udpData.DateTime, cloneUdpData.DateTime); - Assert.NotEqual(udpData.Address, cloneUdpData.Address); - Assert.NotEqual(udpData.Port, cloneUdpData.Port); - Assert.NotEqual(udpData.Data, cloneUdpData.Data); - Assert.NotEqual(udpData.HasRead, cloneUdpData.HasRead); - } - - [Theory] - [InlineData(new object[] { new string[] { "Hello World!", "Hello Server!", "What is your problem?" } })] - public async Task UDPServerFindString(string[] textArray) - { - Assert.True(udpServer.IsRunning); - - var serverEP = new IPEndPoint(IPAddress.Parse(address), port); - - foreach (var text in textArray) - { - Assert.True(await UDPClientPool.SendStringAsync(serverEP, [text])); - var ret = await udpServer.FindDataAsync(address); - Assert.True(ret.HasValue); - var data = ret.Value; - Assert.Equal(address, data.Address); - Assert.Equal(text, Encoding.ASCII.GetString(data.Data)); - } - } - - [Theory] - [InlineData(new object[] { new UInt32[] { 0xF0_00_00_00, 0xFF_00_00_00, 0xFF_FF_FF_FF } })] - public async Task UDPServerFindBytes(UInt32[] bytesArray) - { - Assert.True(udpServer.IsRunning); - - var serverEP = new IPEndPoint(IPAddress.Parse(address), port); - - foreach (var number in bytesArray) - { - Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 4).Value)); - var ret = await udpServer.FindDataAsync(address); - Assert.True(ret.HasValue); - var data = ret.Value; - Assert.Equal(address, data.Address); - Assert.Equal(number, Number.BytesToUInt32(data.Data).Value); - } - } - - [Theory] - [InlineData(new object[] { new UInt32[] { 0xF0_00_00_00, 0xF0_01_00_00 } })] - public async Task UDPServerWaitResp(UInt32[] bytesArray) - { - Assert.True(udpServer.IsRunning); - - var serverEP = new IPEndPoint(IPAddress.Parse(address), port); - - foreach (var number in bytesArray) - { - Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 4).Value)); - - var ret = await udpServer.WaitForAckAsync(address); - Assert.True(ret.IsSuccessful); - var data = ret.Value; - Assert.True(data.IsSuccessful); - Assert.Equal(number, Number.BytesToUInt32(data.ToBytes()).Value); - } - } - - [Theory] - [InlineData(new object[] { new UInt64[] { 0x0F_00_00_00_01_02_02_02, 0x0F_01_00_00_FF_FF_FF_FF } })] - public async Task UDPServerWaitData(UInt64[] bytesArray) - { - Assert.True(udpServer.IsRunning); - - var serverEP = new IPEndPoint(IPAddress.Parse(address), port); - - foreach (var number in bytesArray) - { - Assert.True(await UDPClientPool.SendBytesAsync(serverEP, Number.NumberToBytes(number, 8).Value)); - - var ret = await udpServer.WaitForDataAsync(address); - Assert.True(ret.IsSuccessful); - var data = ret.Value; - Assert.True(data.IsSuccessful); - Assert.Equal((UInt64)number, Number.BytesToUInt64(data.ToBytes()).Value); - } - } -} diff --git a/server/src/Common/Number.cs b/server/src/Common/Number.cs index 0bffc08..7987df0 100644 --- a/server/src/Common/Number.cs +++ b/server/src/Common/Number.cs @@ -65,7 +65,7 @@ public class Number { for (var i = 0; i < length; i++) { - arr[length - 1 - i] = Convert.ToByte((num >> (i << 3)) & (0xFF)); + arr[i] = Convert.ToByte((num >> (i << 3)) & (0xFF)); } } else @@ -99,20 +99,11 @@ public class Number try { - if (isLowNumHigh) + if (!isLowNumHigh) { - for (var i = 0; i < len; i++) - { - num += Convert.ToUInt64((UInt64)bytes[len - 1 - i] << (i << 3)); - } - } - else - { - for (var i = 0; i < len; i++) - { - num += Convert.ToUInt64((UInt64)bytes[i] << ((int)(len - 1 - i) << 3)); - } + Array.Reverse(bytes); } + num = BitConverter.ToUInt64(bytes, 0); return num; } @@ -143,20 +134,11 @@ public class Number try { - if (isLowNumHigh) + if (!isLowNumHigh) { - for (var i = 0; i < len; i++) - { - num += Convert.ToUInt32((UInt32)bytes[len - 1 - i] << (i << 3)); - } - } - else - { - for (var i = 0; i < len; i++) - { - num += Convert.ToUInt32((UInt32)bytes[i] << ((int)(len - 1 - i) << 3)); - } + Array.Reverse(bytes); } + num = BitConverter.ToUInt32(bytes, 0); return num; } @@ -333,10 +315,9 @@ public class Number for (int i = 0; i < srcBytesLen; i += distance) { - var end = i + distance; - buffer = srcBytes[i..end]; + Buffer.BlockCopy(srcBytes, i, buffer, 0, distance); Array.Reverse(buffer); - Array.Copy(buffer, 0, dstBytes, i, distance); + Buffer.BlockCopy(buffer, 0, dstBytes, i, distance); } return dstBytes; diff --git a/server/src/Controllers/DebuggerController.cs b/server/src/Controllers/DebuggerController.cs index 916c500..792164c 100644 --- a/server/src/Controllers/DebuggerController.cs +++ b/server/src/Controllers/DebuggerController.cs @@ -413,6 +413,7 @@ public class DebuggerController : ControllerBase } var channelBytes = new byte[4 * depth]; Buffer.BlockCopy(channelUintArr, 0, channelBytes, 0, channelBytes.Length); + channelBytes = Common.Number.ReverseBytes(channelBytes, 4).Value; logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelBytes)}"); var base64 = Convert.ToBase64String(channelBytes); channelDataList.Add(new ChannelCaptureData { name = channel.name, data = base64 }); From 2adeca3b99c3e14e644a1e7ee6847a0120e98561 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Thu, 31 Jul 2025 16:33:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E9=85=8D=E7=BD=AE=E6=9D=BF?= =?UTF-8?q?=E5=AD=90=E7=BD=91=E7=BB=9C=E6=97=B6=EF=BC=8C=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=8A=A8=E6=80=81mac?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/server.csproj | 1 + server/src/ArpClient.cs | 45 ++++++++++++------- server/src/Controllers/DataController.cs | 9 ++-- server/src/Controllers/DebuggerController.cs | 5 +-- server/src/Controllers/NetConfigController.cs | 13 +++--- server/src/MsgBus.cs | 2 +- src/views/Project/Debugger.vue | 8 ++-- 7 files changed, 46 insertions(+), 37 deletions(-) diff --git a/server/server.csproj b/server/server.csproj index 5c82a95..1341654 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -14,6 +14,7 @@ + diff --git a/server/src/ArpClient.cs b/server/src/ArpClient.cs index 8f04b4a..b6f850a 100644 --- a/server/src/ArpClient.cs +++ b/server/src/ArpClient.cs @@ -1,11 +1,14 @@ using System.Diagnostics; +using System.Net.NetworkInformation; +using ArpLookup; using System.Runtime.InteropServices; +using System.Net; using System.Text.RegularExpressions; /// /// ARP 记录管理静态类(跨平台支持) /// -public static class Arp +public static class ArpClient { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); @@ -44,20 +47,28 @@ public static class Arp } /// - /// 通过 Ping 动态更新指定 IP 的 ARP 记录 + /// 动态更新指定 IP 的 ARP 记录 /// /// 要更新的 IP 地址 /// 是否成功发送 Ping - public static async Task UpdateArpEntryByPingAsync(string ipAddress) + public static async Task UpdateArpEntryAsync(string ipAddress) { if (string.IsNullOrWhiteSpace(ipAddress)) throw new ArgumentException("IP 地址不能为空", nameof(ipAddress)); try { - using var ping = new System.Net.NetworkInformation.Ping(); - var reply = await ping.SendPingAsync(ipAddress, 100); - return reply.Status == System.Net.NetworkInformation.IPStatus.Success; + var ret = await ArpClient.DeleteArpEntryAsync(ipAddress); + if (!ret) + { + logger.Error($"删除 ARP 记录失败: {ipAddress}"); + } + + PhysicalAddress? mac = await Arp.LookupAsync(IPAddress.Parse(ipAddress)); + if (mac == null) + return false; + + return true; } catch { @@ -228,22 +239,22 @@ public static class Arp return null; var lines = result.Output.Split('\n', StringSplitOptions.RemoveEmptyEntries); - + foreach (var line in lines) { // 匹配接口行格式: Interface: 172.6.1.5 --- 0xa var interfacePattern = @"Interface:\s+(\d+\.\d+\.\d+\.\d+)\s+---\s+(0x[a-fA-F0-9]+)"; var match = Regex.Match(line, interfacePattern); - + if (match.Success && match.Groups[1].Value == interfaceIp) { // 将十六进制索引转换为十进制 var hexIndex = match.Groups[2].Value; // 去掉 "0x" 前缀 - var hexValue = hexIndex.StartsWith("0x", StringComparison.OrdinalIgnoreCase) - ? hexIndex.Substring(2) + var hexValue = hexIndex.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? hexIndex.Substring(2) : hexIndex; - + if (int.TryParse(hexValue, System.Globalization.NumberStyles.HexNumber, null, out int decimalIndex)) { logger.Debug($"找到接口 {interfaceIp} 的索引: {hexIndex} -> {decimalIndex}"); @@ -251,7 +262,7 @@ public static class Arp } } } - + logger.Warn($"未找到接口 {interfaceIp} 的索引"); return null; } @@ -417,9 +428,9 @@ public static class Arp private static ArpEntry? ParseWindowsArpEntry(string line) { // 跳过空行和标题行 - if (string.IsNullOrWhiteSpace(line) || - line.Contains("Interface:") || - line.Contains("Internet Address") || + if (string.IsNullOrWhiteSpace(line) || + line.Contains("Interface:") || + line.Contains("Internet Address") || line.Contains("Physical Address") || line.Contains("Type")) { @@ -518,7 +529,7 @@ public static class Arp // 格式化 MAC 地址以适配不同操作系统 string formattedMac = FormatMacAddress(macAddress); - + var entry = await GetArpEntryAsync(ipAddress); if (entry != null && string.Equals(FormatMacAddress(entry.MacAddress), formattedMac, StringComparison.OrdinalIgnoreCase)) { @@ -534,7 +545,7 @@ public static class Arp // 新增 ARP 记录 var ret = await AddArpEntryAsync(ipAddress, formattedMac, interfaceName); - if(!ret) logger.Error($"添加 ARP 记录失败: {ipAddress} -> {formattedMac} on {interfaceName}"); + if (!ret) logger.Error($"添加 ARP 记录失败: {ipAddress} -> {formattedMac} on {interfaceName}"); return true; } diff --git a/server/src/Controllers/DataController.cs b/server/src/Controllers/DataController.cs index ee4f237..0df9499 100644 --- a/server/src/Controllers/DataController.cs +++ b/server/src/Controllers/DataController.cs @@ -51,7 +51,7 @@ public class DataController : ControllerBase public DateTime? BoardExpireTime { get; set; } } - /// + /// /// 获取本机IP地址(优先选择与实验板同网段的IP) /// /// 本机IP地址 @@ -60,7 +60,7 @@ public class DataController : ControllerBase try { var boardIpSegments = BOARD_IP.Split('.').Take(3).ToArray(); - + // 优先选择与实验板IP前三段相同的IP var sameSegmentIP = System.Net.NetworkInformation.NetworkInterface .GetAllNetworkInterfaces() @@ -278,7 +278,7 @@ public class DataController : ControllerBase return NotFound("没有可用的实验板"); var boardInfo = boardOpt.Value; - if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) + if (!(await ArpClient.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } @@ -346,7 +346,7 @@ public class DataController : ControllerBase return NotFound("未找到对应的实验板"); var boardInfo = ret.Value.Value; - if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) + if (!(await ArpClient.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } @@ -490,4 +490,3 @@ public class DataController : ControllerBase } } } - diff --git a/server/src/Controllers/DebuggerController.cs b/server/src/Controllers/DebuggerController.cs index 792164c..44a3876 100644 --- a/server/src/Controllers/DebuggerController.cs +++ b/server/src/Controllers/DebuggerController.cs @@ -380,7 +380,7 @@ public class DebuggerController : ControllerBase } var rawData = dataResult.Value; - logger.Debug($"rawData: {BitConverter.ToString(rawData)}"); + // logger.Debug($"rawData: {BitConverter.ToString(rawData)}"); int depth = (int)config.captureDepth; int portDataLen = 4 * depth; int portNum = (int)config.totalPortNum; @@ -413,8 +413,7 @@ public class DebuggerController : ControllerBase } var channelBytes = new byte[4 * depth]; Buffer.BlockCopy(channelUintArr, 0, channelBytes, 0, channelBytes.Length); - channelBytes = Common.Number.ReverseBytes(channelBytes, 4).Value; - logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelBytes)}"); + // logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelBytes)}"); var base64 = Convert.ToBase64String(channelBytes); channelDataList.Add(new ChannelCaptureData { name = channel.name, data = base64 }); } diff --git a/server/src/Controllers/NetConfigController.cs b/server/src/Controllers/NetConfigController.cs index f0dfad7..5f3679f 100644 --- a/server/src/Controllers/NetConfigController.cs +++ b/server/src/Controllers/NetConfigController.cs @@ -19,8 +19,7 @@ public class NetConfigController : ControllerBase // 固定的实验板IP,端口,MAC地址 private const string BOARD_IP = "169.254.109.0"; private const int BOARD_PORT = 1234; - private const string BOARD_MAC = "12:34:56:78:9a:bc"; - + // 本机网络信息 private readonly IPAddress _localIP; private readonly byte[] _localMAC; @@ -33,14 +32,14 @@ public class NetConfigController : ControllerBase // 初始化本机IP地址 _localIP = GetLocalIPAddress(); _localIPString = _localIP?.ToString() ?? "未知"; - + // 初始化本机MAC地址 _localMAC = GetLocalMACAddress(); _localMACString = _localMAC != null ? BitConverter.ToString(_localMAC).Replace("-", ":") : "未知"; - + // 获取本机网络接口名称 _localInterface = GetLocalNetworkInterface(); - + logger.Info($"NetConfigController 初始化完成 - 本机IP: {_localIPString}, 本机MAC: {_localMACString}, 接口: {_localInterface}"); } @@ -53,7 +52,7 @@ public class NetConfigController : ControllerBase try { var boardIpSegments = BOARD_IP.Split('.').Take(3).ToArray(); - + // 优先选择与实验板IP前三段相同的IP var sameSegmentIP = System.Net.NetworkInformation.NetworkInterface .GetAllNetworkInterfaces() @@ -130,7 +129,7 @@ public class NetConfigController : ControllerBase { try { - return await Arp.CheckOrAddAsync(BOARD_IP, BOARD_MAC, _localInterface); + return await ArpClient.UpdateArpEntryAsync(BOARD_IP); } catch (Exception ex) { diff --git a/server/src/MsgBus.cs b/server/src/MsgBus.cs index c5dff1a..6b8af28 100644 --- a/server/src/MsgBus.cs +++ b/server/src/MsgBus.cs @@ -23,7 +23,7 @@ public static class MsgBus /// public async static void Init() { - if (!Arp.IsAdministrator()) + if (!ArpClient.IsAdministrator()) { logger.Error($"非管理员运行,ARP无法更新,请用管理员权限运行"); // throw new Exception($"非管理员运行,ARP无法更新,请用管理员权限运行"); diff --git a/src/views/Project/Debugger.vue b/src/views/Project/Debugger.vue index c76c34b..0b883d0 100644 --- a/src/views/Project/Debugger.vue +++ b/src/views/Project/Debugger.vue @@ -479,10 +479,10 @@ async function startCapture() { const arr = []; for (let i = 0; i < bin.length; i += 4) { arr.push( - (bin.charCodeAt(i) << 24) | - (bin.charCodeAt(i + 1) << 16) | - (bin.charCodeAt(i + 2) << 8) | - bin.charCodeAt(i + 3), + bin.charCodeAt(i) | + (bin.charCodeAt(i + 1) << 8) | + (bin.charCodeAt(i + 2) << 16) | + (bin.charCodeAt(i + 3) << 24), ); } // 截取采样深度