diff --git a/server/src/Controllers/NetConfigController.cs b/server/src/Controllers/NetConfigController.cs new file mode 100644 index 0000000..c86ef5c --- /dev/null +++ b/server/src/Controllers/NetConfigController.cs @@ -0,0 +1,280 @@ +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Cors; +using Microsoft.AspNetCore.Mvc; +using Peripherals.NetConfigClient; + +namespace server.Controllers; + +/// +/// 网络配置控制器(仅管理员权限) +/// +[ApiController] +[Route("api/[controller]")] +[Authorize("Admin")] +public class NetConfigController : ControllerBase +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + /// + /// 设置主机IP地址 + /// + /// 板卡ID + /// 主机IP地址 + /// 任务ID,默认为0 + /// 操作结果 + [HttpPost("SetHostIP")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task SetHostIP(Guid boardId, string hostIp) + { + if (boardId == Guid.Empty) + return BadRequest("板卡ID不能为空"); + + if (string.IsNullOrWhiteSpace(hostIp)) + return BadRequest("主机IP地址不能为空"); + + if (!IPAddress.TryParse(hostIp, out var ipAddress)) + return BadRequest("IP地址格式不正确"); + + try + { + // 获取板卡信息 + using var db = new Database.AppDataConnection(); + var boardRet = db.GetBoardByID(boardId); + if (!boardRet.IsSuccessful) + return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); + if (!boardRet.Value.HasValue) + return NotFound("未找到对应的板卡"); + + var board = boardRet.Value.Value; + + // 创建网络配置客户端 + var netConfig = new NetConfig(board.IPAddr, board.Port, 0); + var result = await netConfig.SetHostIP(ipAddress); + + if (!result.IsSuccessful) + { + logger.Error($"设置主机IP失败: {result.Error}"); + return StatusCode(StatusCodes.Status500InternalServerError, $"设置失败: {result.Error}"); + } + + return Ok(result.Value); + } + catch (Exception ex) + { + logger.Error(ex, "设置主机IP时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "设置失败,请稍后重试"); + } + } + + /// + /// 设置板卡IP地址 + /// + /// 板卡ID + /// 板卡IP地址 + /// 任务ID,默认为0 + /// 操作结果 + [HttpPost("SetBoardIP")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task SetBoardIP(Guid boardId, string boardIp) + { + if (boardId == Guid.Empty) + return BadRequest("板卡ID不能为空"); + + if (string.IsNullOrWhiteSpace(boardIp)) + return BadRequest("板卡IP地址不能为空"); + + if (!IPAddress.TryParse(boardIp, out var ipAddress)) + return BadRequest("IP地址格式不正确"); + + try + { + // 获取板卡信息 + using var db = new Database.AppDataConnection(); + var boardRet = db.GetBoardByID(boardId); + if (!boardRet.IsSuccessful) + return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); + if (!boardRet.Value.HasValue) + return NotFound("未找到对应的板卡"); + + var board = boardRet.Value.Value; + + // 创建网络配置客户端 + var netConfig = new NetConfig(board.IPAddr, board.Port, 0); + var result = await netConfig.SetBoardIP(ipAddress); + + if (!result.IsSuccessful) + { + logger.Error($"设置板卡IP失败: {result.Error}"); + return StatusCode(StatusCodes.Status500InternalServerError, $"设置失败: {result.Error}"); + } + + return Ok(result.Value); + } + catch (Exception ex) + { + logger.Error(ex, "设置板卡IP时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "设置失败,请稍后重试"); + } + } + + /// + /// 设置主机MAC地址 + /// + /// 板卡ID + /// 主机MAC地址(格式:AA:BB:CC:DD:EE:FF) + /// 任务ID,默认为0 + /// 操作结果 + [HttpPost("SetHostMAC")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task SetHostMAC(Guid boardId, string hostMac) + { + if (boardId == Guid.Empty) + return BadRequest("板卡ID不能为空"); + + if (string.IsNullOrWhiteSpace(hostMac)) + return BadRequest("主机MAC地址不能为空"); + + // 解析MAC地址 + if (!TryParseMacAddress(hostMac, out var macBytes)) + return BadRequest("MAC地址格式不正确,请使用格式:AA:BB:CC:DD:EE:FF"); + + try + { + // 获取板卡信息 + using var db = new Database.AppDataConnection(); + var boardRet = db.GetBoardByID(boardId); + if (!boardRet.IsSuccessful) + return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); + if (!boardRet.Value.HasValue) + return NotFound("未找到对应的板卡"); + + var board = boardRet.Value.Value; + + // 创建网络配置客户端 + var netConfig = new NetConfig(board.IPAddr, board.Port, 0); + var result = await netConfig.SetHostMAC(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地址 + /// + /// 板卡ID + /// 板卡MAC地址(格式:AA:BB:CC:DD:EE:FF) + /// 任务ID,默认为0 + /// 操作结果 + [HttpPost("SetBoardMAC")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task SetBoardMAC(Guid boardId, string boardMac) + { + if (boardId == Guid.Empty) + return BadRequest("板卡ID不能为空"); + + if (string.IsNullOrWhiteSpace(boardMac)) + return BadRequest("板卡MAC地址不能为空"); + + // 解析MAC地址 + if (!TryParseMacAddress(boardMac, out var macBytes)) + return BadRequest("MAC地址格式不正确,请使用格式:AA:BB:CC:DD:EE:FF"); + + try + { + // 获取板卡信息 + using var db = new Database.AppDataConnection(); + var boardRet = db.GetBoardByID(boardId); + if (!boardRet.IsSuccessful) + return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败"); + if (!boardRet.Value.HasValue) + return NotFound("未找到对应的板卡"); + + var board = boardRet.Value.Value; + + // 创建网络配置客户端 + var netConfig = new NetConfig(board.IPAddr, 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地址字符串为字节数组 + /// + /// MAC地址字符串 + /// 解析后的字节数组 + /// 是否解析成功 + private static bool TryParseMacAddress(string macAddress, out byte[] macBytes) + { + macBytes = Array.Empty(); + + if (string.IsNullOrWhiteSpace(macAddress)) + return false; + + // 移除可能的分隔符并统一为冒号 + var cleanMac = macAddress.Replace("-", ":").Replace(" ", "").ToUpper(); + + // 验证格式 + if (cleanMac.Length != 17 || cleanMac.Count(c => c == ':') != 5) + return false; + + var parts = cleanMac.Split(':'); + if (parts.Length != 6) + return false; + + try + { + macBytes = new byte[6]; + for (int i = 0; i < 6; i++) + { + macBytes[i] = Convert.ToByte(parts[i], 16); + } + return true; + } + catch + { + macBytes = Array.Empty(); + return false; + } + } +} \ No newline at end of file diff --git a/server/src/Peripherals/NetConfigClient.cs b/server/src/Peripherals/NetConfigClient.cs new file mode 100644 index 0000000..78d7ffe --- /dev/null +++ b/server/src/Peripherals/NetConfigClient.cs @@ -0,0 +1,174 @@ +using System.Net; +using DotNext; + +namespace Peripherals.NetConfigClient; + +static class NetConfigAddr +{ + const UInt32 BASE = 0x30A7_0000; + + public static readonly UInt32[] HOST_IP = { BASE + 0, BASE + 1, BASE + 2, BASE + 3 }; + public static readonly UInt32[] BOARD_IP = { BASE + 4, BASE + 5, BASE + 6, BASE + 7 }; + public static readonly UInt32[] HOST_MAC = { BASE + 8, BASE + 9, BASE + 10, BASE + 11, BASE + 12, BASE + 13 }; + public static readonly UInt32[] BOARD_MAC = { BASE + 14, BASE + 15, BASE + 16, BASE + 17, BASE + 18, BASE + 19 }; +} + + +/// +/// [TODO:description] +/// +public class NetConfig +{ + private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + readonly int timeout = 2000; + readonly int taskID; + readonly int port; + readonly string address; + private IPEndPoint ep; + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public NetConfig(string address, int port, int taskID, int timeout = 2000) + { + if (timeout < 0) + throw new ArgumentException("Timeout couldn't be negative", nameof(timeout)); + this.address = address; + this.taskID = taskID; + this.port = port; + this.ep = new IPEndPoint(IPAddress.Parse(address), port); + this.timeout = timeout; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> SetHostIP(IPAddress ip) + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ipBytes = ip.GetAddressBytes(); + + { + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_IP, ipBytes, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to set host IP: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error($"Failed to set host IP: operation returned false"); + return false; + } + } + + return true; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> SetBoardIP(IPAddress ip) + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ipBytes = ip.GetAddressBytes(); + + { + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_IP, ipBytes, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to set board IP: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error($"Failed to set board IP: operation returned false"); + return false; + } + } + + return true; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> SetHostMAC(byte[] macAddress) + { + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + if (macAddress.Length != 6) + throw new ArgumentException("MAC address must be 6 bytes", nameof(macAddress)); + + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + { + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_MAC, macAddress, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to set host MAC address: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error($"Failed to set host MAC address: operation returned false"); + return false; + } + } + + return true; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> SetBoardMAC(byte[] macAddress) + { + if (macAddress == null) + throw new ArgumentNullException(nameof(macAddress)); + if (macAddress.Length != 6) + throw new ArgumentException("MAC address must be 6 bytes", nameof(macAddress)); + + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + { + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_MAC, macAddress, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to set board MAC address: {ret.Error}"); + return new(ret.Error); + } + + if (!ret.Value) + { + logger.Error($"Failed to set board MAC address: operation returned false"); + return false; + } + } + + return true; + } +} diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs index 8620c63..3064459 100644 --- a/server/src/UdpClientPool.cs +++ b/server/src/UdpClientPool.cs @@ -559,7 +559,6 @@ public class UDPClientPool IsWrite = true, }; - // Write Register ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts)); if (!ret) return new(new Exception("Send 1st address package failed!")); @@ -602,8 +601,6 @@ public class UDPClientPool IsWrite = true, }; - - // Check Msg Bus if (!MsgBus.IsRunning) return new(new Exception("Message bus not working!")); @@ -644,4 +641,40 @@ public class UDPClientPool return true; } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public static async ValueTask> WriteAddrSeq(IPEndPoint endPoint, int taskID, UInt32[] addr, byte[] data, int timeout = 1000) + { + var length = addr.Length; + if (length != data.Length) + { + logger.Error($"TODO"); + return new(new ArgumentException($"TODO")); + } + + for (int i = 0; i < length; i++) + { + var ret = await WriteAddr(endPoint, taskID, addr[i], (UInt32)data[i], timeout); + if (!ret.IsSuccessful) + { + logger.Error($"TODO"); + return new(ret.Error); + } + if (!ret.Value) + { + logger.Error($"TODO"); + return false; + } + } + + return true; + } } diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs index a2f0452..f5d9323 100644 --- a/server/src/UdpServer.cs +++ b/server/src/UdpServer.cs @@ -266,7 +266,6 @@ public class UDPServer sortedList.Clear(); break; } - } } catch