diff --git a/server/src/ArpClient.cs b/server/src/ArpClient.cs index 3d35969..8f04b4a 100644 --- a/server/src/ArpClient.cs +++ b/server/src/ArpClient.cs @@ -84,7 +84,7 @@ public static class Arp { // 格式化 MAC 地址以适配不同操作系统 string formattedMac = FormatMacAddress(macAddress); - string command = GetArpAddCommand(ipAddress, formattedMac, interfaceName); + string command = await GetArpAddCommandAsync(ipAddress, formattedMac, interfaceName); var result = await ExecuteCommandAsync(command); return result.IsSuccess; } @@ -181,13 +181,20 @@ public static class Arp /// /// 获取 ARP 添加命令 /// - private static string GetArpAddCommand(string ipAddress, string macAddress, string? interfaceName) + private static async Task GetArpAddCommandAsync(string ipAddress, string macAddress, string? interfaceName) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return string.IsNullOrWhiteSpace(interfaceName) - ? $"arp -s {ipAddress} {macAddress}" - : $"arp -s {ipAddress} {macAddress} {interfaceName}"; + if (!string.IsNullOrWhiteSpace(interfaceName)) + { + // 通过 arp -a 获取接口索引 + var interfaceIdx = await GetWindowsInterfaceIndexAsync(interfaceName); + if (interfaceIdx.HasValue) + { + return $"netsh -c i i add neighbors {interfaceIdx.Value} {ipAddress} {macAddress}"; + } + } + return $"arp -s {ipAddress} {macAddress}"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { @@ -207,6 +214,54 @@ public static class Arp } } + /// + /// 获取 Windows 接口索引 + /// + /// 接口IP地址 + /// 接口索引(十进制),如果未找到则返回null + private static async Task GetWindowsInterfaceIndexAsync(string interfaceIp) + { + try + { + var result = await ExecuteCommandAsync("arp -a"); + if (!result.IsSuccess) + 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) + : hexIndex; + + if (int.TryParse(hexValue, System.Globalization.NumberStyles.HexNumber, null, out int decimalIndex)) + { + logger.Debug($"找到接口 {interfaceIp} 的索引: {hexIndex} -> {decimalIndex}"); + return decimalIndex; + } + } + } + + logger.Warn($"未找到接口 {interfaceIp} 的索引"); + return null; + } + catch (Exception ex) + { + logger.Error(ex, $"获取接口 {interfaceIp} 索引失败"); + return null; + } + } + /// /// 获取 ARP 删除命令 /// @@ -478,7 +533,9 @@ public static class Arp } // 新增 ARP 记录 - return await AddArpEntryAsync(ipAddress, formattedMac, interfaceName); + var ret = await AddArpEntryAsync(ipAddress, formattedMac, 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 15a00e3..ee4f237 100644 --- a/server/src/Controllers/DataController.cs +++ b/server/src/Controllers/DataController.cs @@ -1,4 +1,5 @@ using System.IdentityModel.Tokens.Jwt; +using System.Net; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; @@ -16,6 +17,8 @@ namespace server.Controllers; public class DataController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + // 固定的实验板IP,端口,MAC地址 + private const string BOARD_IP = "169.254.109.0"; /// /// [TODO:description] @@ -48,6 +51,53 @@ public class DataController : ControllerBase public DateTime? BoardExpireTime { get; set; } } + /// + /// 获取本机IP地址(优先选择与实验板同网段的IP) + /// + /// 本机IP地址 + private IPAddress GetLocalIPAddress() + { + try + { + var boardIpSegments = BOARD_IP.Split('.').Take(3).ToArray(); + + // 优先选择与实验板IP前三段相同的IP + var sameSegmentIP = System.Net.NetworkInformation.NetworkInterface + .GetAllNetworkInterfaces() + .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up + && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) + .SelectMany(nic => nic.GetIPProperties().UnicastAddresses) + .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + .Select(addr => addr.Address) + .FirstOrDefault(addr => + { + var segments = addr.ToString().Split('.'); + return segments.Length == 4 && + segments[0] == boardIpSegments[0] && + segments[1] == boardIpSegments[1] && + segments[2] == boardIpSegments[2]; + }); + + if (sameSegmentIP != null) + return sameSegmentIP; + + // 如果没有找到同网段的IP,返回第一个可用的IP + return System.Net.NetworkInformation.NetworkInterface + .GetAllNetworkInterfaces() + .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up + && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) + .SelectMany(nic => nic.GetIPProperties().UnicastAddresses) + .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + .Select(addr => addr.Address) + .FirstOrDefault() ?? IPAddress.Loopback; + } + catch (Exception ex) + { + logger.Error(ex, "获取本机IP地址失败"); + return IPAddress.Loopback; + } + } + /// /// 用户登录,获取 JWT 令牌 /// @@ -228,7 +278,7 @@ public class DataController : ControllerBase return NotFound("没有可用的实验板"); var boardInfo = boardOpt.Value; - if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr))) + if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } @@ -296,7 +346,7 @@ public class DataController : ControllerBase return NotFound("未找到对应的实验板"); var boardInfo = ret.Value.Value; - if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr))) + if (!(await Arp.CheckOrAddAsync(boardInfo.IpAddr, boardInfo.MacAddr, GetLocalIPAddress().ToString()))) { logger.Error($"无法配置ARP,实验板可能会无法连接"); } diff --git a/server/src/Controllers/NetConfigController.cs b/server/src/Controllers/NetConfigController.cs index 3adda80..f0dfad7 100644 --- a/server/src/Controllers/NetConfigController.cs +++ b/server/src/Controllers/NetConfigController.cs @@ -119,46 +119,7 @@ public class NetConfigController : ControllerBase /// 网络接口名称 private string GetLocalNetworkInterface() { - try - { - var boardIpSegments = BOARD_IP.Split('.').Take(3).ToArray(); - - // 优先选择与实验板IP前三段相同的网络接口 - var sameSegmentInterface = System.Net.NetworkInformation.NetworkInterface - .GetAllNetworkInterfaces() - .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up - && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) - .FirstOrDefault(nic => - { - var ipAddresses = nic.GetIPProperties().UnicastAddresses - .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - .Select(addr => addr.Address); - - return ipAddresses.Any(addr => - { - var segments = addr.ToString().Split('.'); - return segments.Length == 4 && - segments[0] == boardIpSegments[0] && - segments[1] == boardIpSegments[1] && - segments[2] == boardIpSegments[2]; - }); - }); - - if (sameSegmentInterface != null) - return sameSegmentInterface.Name; - - // 如果没有找到同网段的接口,返回第一个可用的接口 - return System.Net.NetworkInformation.NetworkInterface - .GetAllNetworkInterfaces() - .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up - && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) - .FirstOrDefault()?.Name ?? "未知"; - } - catch (Exception ex) - { - logger.Error(ex, "获取本机网络接口名称失败"); - return "未知"; - } + return GetLocalIPAddress().ToString(); } /// @@ -484,6 +445,50 @@ public class NetConfigController : ControllerBase } } + /// + /// 设置板卡MAC地址 + /// + /// 板卡MAC地址(格式:AA:BB:CC:DD:EE:FF) + /// 操作结果 + [HttpPost("SetBoardMAC")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task SetBoardMAC(string boardMac) + { + if (string.IsNullOrWhiteSpace(boardMac)) + return BadRequest("板卡MAC地址不能为空"); + + // 解析MAC地址 + if (!TryParseMacAddress(boardMac, out var macBytes)) + return BadRequest("MAC地址格式不正确,请使用格式:AA:BB:CC:DD:EE:FF"); + + try + { + if (!(await InitializeArpAsync())) + { + throw new Exception("无法配置ARP记录"); + } + // 创建网络配置客户端 + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.SetBoardMAC(macBytes); + + if (!result.IsSuccessful) + { + logger.Error($"设置板卡MAC地址失败: {result.Error}"); + return StatusCode(StatusCodes.Status500InternalServerError, $"设置失败: {result.Error}"); + } + + return Ok(result.Value); + } + catch (Exception ex) + { + logger.Error(ex, "设置板卡MAC地址时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "设置失败,请稍后重试"); + } + } + /// /// 设置主机MAC地址 /// diff --git a/server/src/Peripherals/CameraClient.cs b/server/src/Peripherals/CameraClient.cs index 65b8609..855808a 100644 --- a/server/src/Peripherals/CameraClient.cs +++ b/server/src/Peripherals/CameraClient.cs @@ -8,11 +8,11 @@ static class CameraAddr { public const UInt32 BASE = 0x7000_0000; - public const UInt32 STORE_ADDR = BASE + 0x12; - public const UInt32 STORE_NUM = BASE + 0x13; - public const UInt32 EXPECTED_VH = BASE + 0x14; - public const UInt32 CAPTURE_ON = BASE + 0x15; - public const UInt32 CAMERA_POWER = BASE + 0x16; //[0]: rstn, 0 is reset. [8]: power down, 1 is down. + public const UInt32 DMA0_START_WRITE_ADDR = BASE + 0x0C; + public const UInt32 DMA0_END_WRITE_ADDR = BASE + 0x0D; + public const UInt32 DMA0_CAPTURE_CTRL = BASE + 0x0E; //[0]: on, 1 is on. [8]: reset, 1 is reset. + public const UInt32 EXPECTED_VH = BASE + 0x0F; + public const UInt32 CAMERA_POWER = BASE + 0x10; //[0]: rstn, 0 is reset. [8]: power down, 1 is down. } class Camera @@ -156,7 +156,7 @@ class Camera public async ValueTask> EnableHardwareTrans(bool isEnable) { - var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.CAPTURE_ON, Convert.ToUInt32(isEnable)); + var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.DMA0_CAPTURE_CTRL, (isEnable ? 0x00000001u : 0x00000100u)); if (!ret.IsSuccessful) { logger.Error($"Failed to write CAPTURE_ON to camera at {this.address}:{this.port}, error: {ret.Error}"); @@ -361,7 +361,7 @@ class Camera // 1. 配置UDP相关寄存器 { - var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_ADDR, FrameAddr); + var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.DMA0_START_WRITE_ADDR, FrameAddr); if (!ret.IsSuccessful) { logger.Error($"Failed to write STORE_ADDR: {ret.Error}"); @@ -375,7 +375,7 @@ class Camera } { - var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.STORE_NUM, frameLength); + var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, CameraAddr.DMA0_END_WRITE_ADDR, FrameAddr + frameLength - 1); if (!ret.IsSuccessful) { logger.Error($"Failed to write STORE_NUM: {ret.Error}"); @@ -635,7 +635,7 @@ class Camera { var basicRegisters = new UInt16[][] { - [0x3103, 0x03], // system clock from pad, bit[1] + [0x3103, 0x03], // system clock from pad, bit[1] //02 [0x3017, 0xff], [0x3018, 0xff], [0x3037, 0x13], @@ -750,6 +750,7 @@ class Camera [0x3c04, 0x28], [0x3c05, 0x98], [0x3c06, 0x00], + [0x3c07, 0x07], [0x3c08, 0x00], [0x3c09, 0x1c], [0x3c0a, 0x9c], @@ -803,12 +804,12 @@ class Camera { var aecRegisters = new UInt16[][] { - [0x3a0f, 0x30], // AEC控制;stable range in high - [0x3a10, 0x28], // AEC控制;stable range in low - [0x3a1b, 0x30], // AEC控制;stable range out high - [0x3a1e, 0x26], // AEC控制;stable range out low - [0x3a11, 0x60], // AEC控制; fast zone high - [0x3a1f, 0x14], // AEC控制; fast zone low + [0x3a0f, 0x30], // AEC控制;stable range in high //78 + [0x3a10, 0x28], // AEC控制;stable range in low //68 + [0x3a1b, 0x30], // AEC控制;stable range out high //78 + [0x3a1e, 0x26], // AEC控制;stable range out low //68 + [0x3a11, 0x60], // AEC控制; fast zone high //D0 + [0x3a1f, 0x14], // AEC控制; fast zone low //40 [0x3b07, 0x0a] // 帧曝光模式 }; @@ -952,7 +953,7 @@ class Camera { var timingRegisters = new UInt16[][] { - [0x3035, 0x41], // 60fps + [0x3035, 0x21], // 60fps [0x3036, PLL_MUX],// PLL倍频 [0x3c07, 0x08], [0x3820, 0x41], // vflip diff --git a/src/views/User/Index.vue b/src/views/User/Index.vue index e979e69..cb2116f 100644 --- a/src/views/User/Index.vue +++ b/src/views/User/Index.vue @@ -1,6 +1,6 @@