feat: 使用netsh指令设置arp

This commit is contained in:
alivender 2025-07-18 14:37:25 +08:00
parent 35bad4027d
commit ba79a2093b
5 changed files with 179 additions and 66 deletions

View File

@ -84,7 +84,7 @@ public static class Arp
{ {
// 格式化 MAC 地址以适配不同操作系统 // 格式化 MAC 地址以适配不同操作系统
string formattedMac = FormatMacAddress(macAddress); string formattedMac = FormatMacAddress(macAddress);
string command = GetArpAddCommand(ipAddress, formattedMac, interfaceName); string command = await GetArpAddCommandAsync(ipAddress, formattedMac, interfaceName);
var result = await ExecuteCommandAsync(command); var result = await ExecuteCommandAsync(command);
return result.IsSuccess; return result.IsSuccess;
} }
@ -181,13 +181,20 @@ public static class Arp
/// <summary> /// <summary>
/// 获取 ARP 添加命令 /// 获取 ARP 添加命令
/// </summary> /// </summary>
private static string GetArpAddCommand(string ipAddress, string macAddress, string? interfaceName) private static async Task<string> GetArpAddCommandAsync(string ipAddress, string macAddress, string? interfaceName)
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
return string.IsNullOrWhiteSpace(interfaceName) if (!string.IsNullOrWhiteSpace(interfaceName))
? $"arp -s {ipAddress} {macAddress}" {
: $"arp -s {ipAddress} {macAddress} {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)) else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{ {
@ -207,6 +214,54 @@ public static class Arp
} }
} }
/// <summary>
/// 获取 Windows 接口索引
/// </summary>
/// <param name="interfaceIp">接口IP地址</param>
/// <returns>接口索引十进制如果未找到则返回null</returns>
private static async Task<int?> 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;
}
}
/// <summary> /// <summary>
/// 获取 ARP 删除命令 /// 获取 ARP 删除命令
/// </summary> /// </summary>
@ -478,7 +533,9 @@ public static class Arp
} }
// 新增 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;
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,5 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@ -16,6 +17,8 @@ namespace server.Controllers;
public class DataController : ControllerBase public class DataController : ControllerBase
{ {
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
// 固定的实验板IP,端口,MAC地址
private const string BOARD_IP = "169.254.109.0";
/// <summary> /// <summary>
/// [TODO:description] /// [TODO:description]
@ -48,6 +51,53 @@ public class DataController : ControllerBase
public DateTime? BoardExpireTime { get; set; } public DateTime? BoardExpireTime { get; set; }
} }
/// <summary>
/// 获取本机IP地址优先选择与实验板同网段的IP
/// </summary>
/// <returns>本机IP地址</returns>
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;
}
}
/// <summary> /// <summary>
/// 用户登录,获取 JWT 令牌 /// 用户登录,获取 JWT 令牌
/// </summary> /// </summary>
@ -228,7 +278,7 @@ public class DataController : ControllerBase
return NotFound("没有可用的实验板"); return NotFound("没有可用的实验板");
var boardInfo = boardOpt.Value; 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实验板可能会无法连接"); logger.Error($"无法配置ARP实验板可能会无法连接");
} }
@ -296,7 +346,7 @@ public class DataController : ControllerBase
return NotFound("未找到对应的实验板"); return NotFound("未找到对应的实验板");
var boardInfo = ret.Value.Value; 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实验板可能会无法连接"); logger.Error($"无法配置ARP实验板可能会无法连接");
} }

View File

@ -119,46 +119,7 @@ public class NetConfigController : ControllerBase
/// <returns>网络接口名称</returns> /// <returns>网络接口名称</returns>
private string GetLocalNetworkInterface() private string GetLocalNetworkInterface()
{ {
try return GetLocalIPAddress().ToString();
{
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 "未知";
}
} }
/// <summary> /// <summary>
@ -484,6 +445,50 @@ public class NetConfigController : ControllerBase
} }
} }
/// <summary>
/// 设置板卡MAC地址
/// </summary>
/// <param name="boardMac">板卡MAC地址格式AA:BB:CC:DD:EE:FF</param>
/// <returns>操作结果</returns>
[HttpPost("SetBoardMAC")]
[EnableCors("Users")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> 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, "设置失败,请稍后重试");
}
}
/// <summary> /// <summary>
/// 设置主机MAC地址 /// 设置主机MAC地址
/// </summary> /// </summary>

View File

@ -8,11 +8,11 @@ static class CameraAddr
{ {
public const UInt32 BASE = 0x7000_0000; public const UInt32 BASE = 0x7000_0000;
public const UInt32 STORE_ADDR = BASE + 0x12; public const UInt32 DMA0_START_WRITE_ADDR = BASE + 0x0C;
public const UInt32 STORE_NUM = BASE + 0x13; public const UInt32 DMA0_END_WRITE_ADDR = BASE + 0x0D;
public const UInt32 EXPECTED_VH = BASE + 0x14; public const UInt32 DMA0_CAPTURE_CTRL = BASE + 0x0E; //[0]: on, 1 is on. [8]: reset, 1 is reset.
public const UInt32 CAPTURE_ON = BASE + 0x15; public const UInt32 EXPECTED_VH = BASE + 0x0F;
public const UInt32 CAMERA_POWER = BASE + 0x16; //[0]: rstn, 0 is reset. [8]: power down, 1 is down. public const UInt32 CAMERA_POWER = BASE + 0x10; //[0]: rstn, 0 is reset. [8]: power down, 1 is down.
} }
class Camera class Camera
@ -156,7 +156,7 @@ class Camera
public async ValueTask<Result<bool>> EnableHardwareTrans(bool isEnable) public async ValueTask<Result<bool>> 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) if (!ret.IsSuccessful)
{ {
logger.Error($"Failed to write CAPTURE_ON to camera at {this.address}:{this.port}, error: {ret.Error}"); 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相关寄存器 // 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) if (!ret.IsSuccessful)
{ {
logger.Error($"Failed to write STORE_ADDR: {ret.Error}"); 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) if (!ret.IsSuccessful)
{ {
logger.Error($"Failed to write STORE_NUM: {ret.Error}"); logger.Error($"Failed to write STORE_NUM: {ret.Error}");
@ -635,7 +635,7 @@ class Camera
{ {
var basicRegisters = new UInt16[][] var basicRegisters = new UInt16[][]
{ {
[0x3103, 0x03], // system clock from pad, bit[1] [0x3103, 0x03], // system clock from pad, bit[1] //02
[0x3017, 0xff], [0x3017, 0xff],
[0x3018, 0xff], [0x3018, 0xff],
[0x3037, 0x13], [0x3037, 0x13],
@ -750,6 +750,7 @@ class Camera
[0x3c04, 0x28], [0x3c04, 0x28],
[0x3c05, 0x98], [0x3c05, 0x98],
[0x3c06, 0x00], [0x3c06, 0x00],
[0x3c07, 0x07],
[0x3c08, 0x00], [0x3c08, 0x00],
[0x3c09, 0x1c], [0x3c09, 0x1c],
[0x3c0a, 0x9c], [0x3c0a, 0x9c],
@ -803,12 +804,12 @@ class Camera
{ {
var aecRegisters = new UInt16[][] var aecRegisters = new UInt16[][]
{ {
[0x3a0f, 0x30], // AEC控制;stable range in high [0x3a0f, 0x30], // AEC控制;stable range in high //78
[0x3a10, 0x28], // AEC控制;stable range in low [0x3a10, 0x28], // AEC控制;stable range in low //68
[0x3a1b, 0x30], // AEC控制;stable range out high [0x3a1b, 0x30], // AEC控制;stable range out high //78
[0x3a1e, 0x26], // AEC控制;stable range out low [0x3a1e, 0x26], // AEC控制;stable range out low //68
[0x3a11, 0x60], // AEC控制; fast zone high [0x3a11, 0x60], // AEC控制; fast zone high //D0
[0x3a1f, 0x14], // AEC控制; fast zone low [0x3a1f, 0x14], // AEC控制; fast zone low //40
[0x3b07, 0x0a] // 帧曝光模式 [0x3b07, 0x0a] // 帧曝光模式
}; };
@ -952,7 +953,7 @@ class Camera
{ {
var timingRegisters = new UInt16[][] var timingRegisters = new UInt16[][]
{ {
[0x3035, 0x41], // 60fps [0x3035, 0x21], // 60fps
[0x3036, PLL_MUX],// PLL倍频 [0x3036, PLL_MUX],// PLL倍频
[0x3c07, 0x08], [0x3c07, 0x08],
[0x3820, 0x41], // vflip [0x3820, 0x41], // vflip

View File

@ -1,6 +1,6 @@
<template> <template>
<div <div
class="min-h-screen bg-base-100 container mx-auto p-6 space-y-6 flex flex-row" class="min-h-screen bg-base-100 container-md mx-auto p-6 space-y-6 flex flex-row"
> >
<ul class="menu bg-base-200 w-56 gap-2 rounded-2xl p-5"> <ul class="menu bg-base-200 w-56 gap-2 rounded-2xl p-5">
<li id="1" @click="setActivePage"> <li id="1" @click="setActivePage">
@ -14,7 +14,7 @@
</li> </li>
</ul> </ul>
<div class="divider divider-horizontal h-full"></div> <div class="divider divider-horizontal h-full"></div>
<div class="card bg-base-300 w-300 rounded-2xl p-7"> <div class="card bg-base-300 flex-1 rounded-2xl p-7">
<div v-if="activePage === 1"> <div v-if="activePage === 1">
<UserInfo /> <UserInfo />
</div> </div>