diff --git a/server/src/Controllers/DataController.cs b/server/src/Controllers/DataController.cs index 91b2a3c..f34fe19 100644 --- a/server/src/Controllers/DataController.cs +++ b/server/src/Controllers/DataController.cs @@ -222,7 +222,7 @@ public class DataController : ControllerBase var user = userRet.Value.Value; var expireTime = DateTime.UtcNow.AddHours(durationHours); - + var boardOpt = db.GetAvailableBoard(user.ID, expireTime); if (!boardOpt.HasValue) return NotFound("没有可用的实验板"); @@ -303,21 +303,17 @@ public class DataController : ControllerBase [Authorize("Admin")] [HttpPost("AddBoard")] [EnableCors("Users")] - [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(Guid), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public IActionResult AddBoard(string name, string ipAddr, int port) + public IActionResult AddBoard(string name) { if (string.IsNullOrWhiteSpace(name)) return BadRequest("板子名称不能为空"); - if (string.IsNullOrWhiteSpace(ipAddr)) - return BadRequest("IP地址不能为空"); - if (port <= 0 || port > 65535) - return BadRequest("端口号不合法"); try { using var db = new Database.AppDataConnection(); - var ret = db.AddBoard(name, ipAddr, port); + var ret = db.AddBoard(name); return Ok(ret); } catch (Exception ex) diff --git a/server/src/Controllers/NetConfigController.cs b/server/src/Controllers/NetConfigController.cs index ec9b2fc..2ebfc01 100644 --- a/server/src/Controllers/NetConfigController.cs +++ b/server/src/Controllers/NetConfigController.cs @@ -15,11 +15,206 @@ namespace server.Controllers; public class NetConfigController : ControllerBase { private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); - + // 固定的实验板IP和端口 private const string BOARD_IP = "169.254.109.0"; private const int BOARD_PORT = 1234; + /// + /// 获取主机IP地址 + /// + /// 主机IP地址 + [HttpGet("GetHostIP")] + [EnableCors("Users")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task GetHostIP() + { + try + { + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.GetHostIP(); + + 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地址 + /// + /// 板卡IP地址 + [HttpGet("GetBoardIP")] + [EnableCors("Users")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task GetBoardIP() + { + try + { + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.GetBoardIP(); + + 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地址 + /// + /// 主机MAC地址 + [HttpGet("GetHostMAC")] + [EnableCors("Users")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task GetHostMAC() + { + try + { + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.GetHostMAC(); + + 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地址 + [HttpGet("GetBoardMAC")] + [EnableCors("Users")] + [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task GetBoardMAC() + { + try + { + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.GetBoardMAC(); + + 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, "获取失败,请稍后重试"); + } + } + + /// + /// 获取所有网络配置信息 + /// + /// 网络配置信息 + [HttpGet("GetNetworkConfig")] + [EnableCors("Users")] + [ProducesResponseType(typeof(NetworkConfigDto), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task GetNetworkConfig() + { + try + { + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + + var hostIPResult = await netConfig.GetHostIP(); + var boardIPResult = await netConfig.GetBoardIP(); + var hostMACResult = await netConfig.GetHostMAC(); + var boardMACResult = await netConfig.GetBoardMAC(); + + var config = new NetworkConfigDto + { + HostIP = hostIPResult.IsSuccessful ? hostIPResult.Value : "获取失败", + BoardIP = boardIPResult.IsSuccessful ? boardIPResult.Value : "获取失败", + HostMAC = hostMACResult.IsSuccessful ? hostMACResult.Value : "获取失败", + BoardMAC = boardMACResult.IsSuccessful ? boardMACResult.Value : "获取失败" + }; + + return Ok(config); + } + catch (Exception ex) + { + logger.Error(ex, "获取网络配置信息时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); + } + } + + /// + /// 获取本机所有网络接口信息 + /// + /// 网络接口信息列表 + [HttpGet("GetLocalNetworkInterfaces")] + [EnableCors("Users")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public IActionResult GetLocalNetworkInterfaces() + { + try + { + var interfaces = System.Net.NetworkInformation.NetworkInterface + .GetAllNetworkInterfaces() + .Where(nic => nic.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up + && nic.NetworkInterfaceType != System.Net.NetworkInformation.NetworkInterfaceType.Loopback) + .Select(nic => new NetworkInterfaceDto + { + Name = nic.Name, + Description = nic.Description, + Type = nic.NetworkInterfaceType.ToString(), + Status = nic.OperationalStatus.ToString(), + IPAddresses = nic.GetIPProperties().UnicastAddresses + .Where(addr => addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + .Select(addr => addr.Address.ToString()) + .ToList(), + MACAddress = nic.GetPhysicalAddress().ToString() + }) + .ToList(); + + return Ok(interfaces); + } + catch (Exception ex) + { + logger.Error(ex, "获取本机网络接口信息时发生异常"); + return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试"); + } + } + /// /// 设置主机IP地址 /// @@ -138,6 +333,49 @@ public class NetConfigController : ControllerBase } } + /// + /// 自动获取本机IP地址并设置为实验板主机IP + /// + /// 操作结果 + [HttpPost("UpdateHostIP")] + [EnableCors("Users")] + [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task UpdateHostIP() + { + try + { + // 获取本机第一个有效的IPv4地址 + var ip = 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(); + + if (ip == null) + return StatusCode(StatusCodes.Status500InternalServerError, "无法获取本机IP地址"); + + var netConfig = new NetConfig(BOARD_IP, BOARD_PORT, 0); + var result = await netConfig.SetHostIP(ip); + + 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地址 /// @@ -262,3 +500,111 @@ public class NetConfigController : ControllerBase } } } + +/// +/// 网络配置数据传输对象 +/// +public class NetworkConfigDto +{ + /// + /// 主机IP地址 + /// + public string? HostIP { get; set; } + + /// + /// 板卡IP地址 + /// + public string? BoardIP { get; set; } + + /// + /// 主机MAC地址 + /// + public string? HostMAC { get; set; } + + /// + /// 板卡MAC地址 + /// + public string? BoardMAC { get; set; } +} + +/// +/// 网络配置操作结果 +/// +public class NetworkConfigResult +{ + /// + /// 主机IP设置结果 + /// + public bool? HostIPResult { get; set; } + + /// + /// 主机IP设置错误信息 + /// + public string? HostIPError { get; set; } + + /// + /// 板卡IP设置结果 + /// + public bool? BoardIPResult { get; set; } + + /// + /// 板卡IP设置错误信息 + /// + public string? BoardIPError { get; set; } + + /// + /// 主机MAC设置结果 + /// + public bool? HostMACResult { get; set; } + + /// + /// 主机MAC设置错误信息 + /// + public string? HostMACError { get; set; } + + /// + /// 板卡MAC设置结果 + /// + public bool? BoardMACResult { get; set; } + + /// + /// 板卡MAC设置错误信息 + /// + public string? BoardMACError { get; set; } +} + +/// +/// 网络接口信息数据传输对象 +/// +public class NetworkInterfaceDto +{ + /// + /// 网络接口名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 网络接口描述 + /// + public string Description { get; set; } = string.Empty; + + /// + /// 网络接口类型 + /// + public string Type { get; set; } = string.Empty; + + /// + /// 网络接口状态 + /// + public string Status { get; set; } = string.Empty; + + /// + /// IP地址列表 + /// + public List IPAddresses { get; set; } = new(); + + /// + /// MAC地址 + /// + public string MACAddress { get; set; } = string.Empty; +} diff --git a/server/src/Database.cs b/server/src/Database.cs index 084853b..510ff64 100644 --- a/server/src/Database.cs +++ b/server/src/Database.cs @@ -92,11 +92,17 @@ public class Board [NotNull] public required string IpAddr { get; set; } + /// + /// FPGA 板子的MAC地址 + /// + [NotNull] + public required string MacAddr { get; set; } + /// /// FPGA 板子的通信端口 /// [NotNull] - public required int Port { get; set; } + public int Port { get; set; } = 1234; /// /// FPGA 板子的当前状态 @@ -127,6 +133,11 @@ public class Board /// public enum BoardStatus { + /// + /// 未启用状态,无法被使用 + /// + Disabled, + /// /// 繁忙状态,正在被用户使用 /// @@ -371,25 +382,61 @@ public class AppDataConnection : DataConnection return userResult + boardResult; } + /// + /// 自动分配一个未被占用的IP地址 + /// + /// 分配的IP地址字符串 + public string AllocateIpAddr() + { + var usedIps = this.BoardTable.Select(b => b.IpAddr).ToArray(); + for (int i = 1; i <= 254; i++) + { + string ip = $"169.254.109.{i}"; + if (!usedIps.Contains(ip)) + return ip; + } + throw new Exception("没有可用的IP地址"); + } + + /// + /// 自动分配一个未被占用的MAC地址 + /// + /// 分配的MAC地址字符串 + public string AllocateMacAddr() + { + var usedMacs = this.BoardTable.Select(b => b.MacAddr).ToArray(); + // 以 02-00-00-xx-xx-xx 格式分配,02 表示本地管理地址 + for (int i = 1; i <= 0xFFFFFF; i++) + { + string mac = $"02-00-00-{(i >> 16) & 0xFF:X2}-{(i >> 8) & 0xFF:X2}-{i & 0xFF:X2}"; + if (!usedMacs.Contains(mac)) + return mac; + } + throw new Exception("没有可用的MAC地址"); + } + /// /// 添加一块新的 FPGA 板子到数据库 /// /// FPGA 板子的名称 - /// FPGA 板子的IP地址 - /// FPGA 板子的通信端口 /// 插入的记录数 - public int AddBoard(string name, string ipAddr, int port) + public Guid AddBoard(string name) { + if (string.IsNullOrWhiteSpace(name) || name.Contains('\'') || name.Contains(';')) + { + logger.Error("实验板名称非法,包含不允许的字符"); + throw new ArgumentException("实验板名称非法"); + } var board = new Board() { BoardName = name, - IpAddr = ipAddr, - Port = port, - Status = Database.Board.BoardStatus.Available, + IpAddr = AllocateIpAddr(), + MacAddr = AllocateMacAddr(), + Status = Database.Board.BoardStatus.Disabled, }; var result = this.Insert(board); - logger.Info($"新实验板已添加: {name} ({ipAddr}:{port})"); - return result; + logger.Info($"新实验板已添加: {name} ({board.IpAddr}:{board.MacAddr})"); + return board.ID; } /// @@ -543,18 +590,39 @@ public class AppDataConnection : DataConnection } /// - /// 更新实验板的IP地址 + /// [TODO:description] /// - /// 实验板的唯一标识符 - /// 新的IP地址 - /// 更新的记录数 - public int UpdateBoardIpAddr(Guid boardId, string newIpAddr) + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public int UpdateBoardName(Guid boardId, string newName) + { + if (string.IsNullOrWhiteSpace(newName) || newName.Contains('\'') || newName.Contains(';')) + { + logger.Error("实验板名称非法,包含不允许的字符"); + return 0; + } + var result = this.BoardTable + .Where(b => b.ID == boardId) + .Set(b => b.BoardName, newName) + .Update(); + logger.Info($"实验板名称已更新: {boardId} -> {newName}"); + return result; + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public int UpdateBoardStatus(Guid boardId, Board.BoardStatus newStatus) { var result = this.BoardTable .Where(b => b.ID == boardId) - .Set(b => b.IpAddr, newIpAddr) + .Set(b => b.Status, newStatus) .Update(); - logger.Info($"实验板 {boardId} 的IP地址已更新为 {newIpAddr},更新记录数: {result}"); + logger.Info($"TODO"); return result; } diff --git a/server/src/Peripherals/NetConfigClient.cs b/server/src/Peripherals/NetConfigClient.cs index 78d7ffe..a05b776 100644 --- a/server/src/Peripherals/NetConfigClient.cs +++ b/server/src/Peripherals/NetConfigClient.cs @@ -13,9 +13,8 @@ static class NetConfigAddr public static readonly UInt32[] BOARD_MAC = { BASE + 14, BASE + 15, BASE + 16, BASE + 17, BASE + 18, BASE + 19 }; } - /// -/// [TODO:description] +/// Network configuration client for FPGA board communication /// public class NetConfig { @@ -28,13 +27,12 @@ public class NetConfig private IPEndPoint ep; /// - /// [TODO:description] + /// Initialize NetConfig client /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// Target board address + /// Target board port + /// Task identifier + /// Timeout in milliseconds public NetConfig(string address, int port, int taskID, int timeout = 2000) { if (timeout < 0) @@ -47,10 +45,10 @@ public class NetConfig } /// - /// [TODO:description] + /// Set host IP address /// - /// [TODO:parameter] - /// [TODO:return] + /// IP address to set + /// Result indicating success or failure public async ValueTask> SetHostIP(IPAddress ip) { // 清除UDP服务器接收缓冲区 @@ -58,29 +56,43 @@ public class NetConfig var ipBytes = ip.GetAddressBytes(); + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_IP, ipBytes, this.timeout); + if (!ret.IsSuccessful) { - 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; - } + 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; + } + + // 验证设置结果 + var verifyResult = await GetHostIP(); + if (!verifyResult.IsSuccessful) + { + logger.Error($"Failed to verify host IP after setting: {verifyResult.Error}"); + return new(verifyResult.Error); + } + + var expectedIP = ip.ToString(); + if (verifyResult.Value != expectedIP) + { + logger.Error($"Host IP verification failed: expected {expectedIP}, got {verifyResult.Value}"); + return false; + } + + logger.Info($"Successfully set and verified host IP: {expectedIP}"); return true; } /// - /// [TODO:description] + /// Set board IP address /// - /// [TODO:parameter] - /// [TODO:return] + /// IP address to set + /// Result indicating success or failure public async ValueTask> SetBoardIP(IPAddress ip) { // 清除UDP服务器接收缓冲区 @@ -88,29 +100,43 @@ public class NetConfig var ipBytes = ip.GetAddressBytes(); + var ret = await UDPClientPool.WriteAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_IP, ipBytes, this.timeout); + if (!ret.IsSuccessful) { - 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; - } + 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; + } + + // 验证设置结果 + var verifyResult = await GetBoardIP(); + if (!verifyResult.IsSuccessful) + { + logger.Error($"Failed to verify board IP after setting: {verifyResult.Error}"); + return new(verifyResult.Error); + } + + var expectedIP = ip.ToString(); + if (verifyResult.Value != expectedIP) + { + logger.Error($"Board IP verification failed: expected {expectedIP}, got {verifyResult.Value}"); + return false; + } + + logger.Info($"Successfully set and verified board IP: {expectedIP}"); return true; } /// - /// [TODO:description] + /// Set host MAC address /// - /// [TODO:parameter] - /// [TODO:return] + /// MAC address bytes (6 bytes) + /// Result indicating success or failure public async ValueTask> SetHostMAC(byte[] macAddress) { if (macAddress == null) @@ -121,29 +147,43 @@ public class NetConfig // 清除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) { - 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; - } + 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; + } + + // 验证设置结果 + var verifyResult = await GetHostMAC(); + if (!verifyResult.IsSuccessful) + { + logger.Error($"Failed to verify host MAC after setting: {verifyResult.Error}"); + return new(verifyResult.Error); + } + + var expectedMAC = string.Join(":", macAddress.Select(b => $"{b:X2}")); + if (verifyResult.Value != expectedMAC) + { + logger.Error($"Host MAC verification failed: expected {expectedMAC}, got {verifyResult.Value}"); + return false; + } + + logger.Info($"Successfully set and verified host MAC: {expectedMAC}"); return true; } /// - /// [TODO:description] + /// Set board MAC address /// - /// [TODO:parameter] - /// [TODO:return] + /// MAC address bytes (6 bytes) + /// Result indicating success or failure public async ValueTask> SetBoardMAC(byte[] macAddress) { if (macAddress == null) @@ -154,21 +194,143 @@ public class NetConfig // 清除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) { - 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; - } + 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; + } + + // 验证设置结果 + var verifyResult = await GetBoardMAC(); + if (!verifyResult.IsSuccessful) + { + logger.Error($"Failed to verify board MAC after setting: {verifyResult.Error}"); + return new(verifyResult.Error); + } + + var expectedMAC = string.Join(":", macAddress.Select(b => $"{b:X2}")); + if (verifyResult.Value != expectedMAC) + { + logger.Error($"Board MAC verification failed: expected {expectedMAC}, got {verifyResult.Value}"); + return false; + } + + logger.Info($"Successfully set and verified board MAC: {expectedMAC}"); return true; } + + /// + /// Get host IP address + /// + /// Host IP address as string + public async ValueTask> GetHostIP() + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_IP, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to get host IP: {ret.Error}"); + return new(ret.Error); + } + + var ip = ""; + for (int i = 0; i < NetConfigAddr.HOST_IP.Length; i++) + { + ip += $"{ret.Value[i * 4 + 3]}"; + if (i != NetConfigAddr.HOST_IP.Length - 1) + ip += "."; + } + + return ip; + } + + /// + /// Get board IP address + /// + /// Board IP address as string + public async ValueTask> GetBoardIP() + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_IP, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to get board IP: {ret.Error}"); + return new(ret.Error); + } + + var ip = ""; + for (int i = 0; i < NetConfigAddr.BOARD_IP.Length; i++) + { + ip += $"{ret.Value[i * 4 + 3]}"; + if (i != NetConfigAddr.BOARD_IP.Length - 1) + ip += "."; + } + + return ip; + } + + /// + /// Get host MAC address + /// + /// Host MAC address as formatted string (XX:XX:XX:XX:XX:XX) + public async ValueTask> GetHostMAC() + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.HOST_MAC, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to get host MAC address: {ret.Error}"); + return new(ret.Error); + } + + var mac = ""; + for (int i = 0; i < NetConfigAddr.HOST_MAC.Length; i++) + { + mac += $"{ret.Value[i * 4 + 3]:X2}"; + if (i != NetConfigAddr.HOST_MAC.Length - 1) + mac += ":"; + } + + return mac; + } + + /// + /// Get board MAC address + /// + /// Board MAC address as formatted string (XX:XX:XX:XX:XX:XX) + public async ValueTask> GetBoardMAC() + { + // 清除UDP服务器接收缓冲区 + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + + var ret = await UDPClientPool.ReadAddrSeq(this.ep, this.taskID, NetConfigAddr.BOARD_MAC, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Failed to get board MAC address: {ret.Error}"); + return new(ret.Error); + } + + var mac = ""; + for (int i = 0; i < NetConfigAddr.BOARD_MAC.Length; i++) + { + mac += $"{ret.Value[i * 4 + 3]:X2}"; + if (i != NetConfigAddr.BOARD_MAC.Length - 1) + mac += ":"; + } + + return mac; + } } diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs index 3064459..4b7712b 100644 --- a/server/src/UdpClientPool.cs +++ b/server/src/UdpClientPool.cs @@ -537,6 +537,42 @@ public class UDPClientPool return resultData.ToArray(); } + /// + /// 顺序读取多个地址的数据,并合并BodyData后返回 + /// + /// IP端点(IP地址与端口) + /// 任务ID + /// 地址数组 + /// 超时时间(毫秒) + /// 合并后的BodyData字节数组 + public static async ValueTask> ReadAddrSeq(IPEndPoint endPoint, int taskID, UInt32[] addr, int timeout = 1000) + { + var length = addr.Length; + var resultData = new List(); + for (int i = 0; i < length; i++) + { + var ret = await ReadAddr(endPoint, taskID, addr[i], timeout); + if (!ret.IsSuccessful) + { + logger.Error($"ReadAddrSeq failed at index {i}: {ret.Error}"); + return new(ret.Error); + } + if (!ret.Value.IsSuccessful) + { + logger.Error($"ReadAddrSeq failed at index {i}: Read not successful"); + return new(new Exception($"ReadAddrSeq failed at index {i}")); + } + var data = ret.Value.Options.Data; + if (data is null) + { + logger.Error($"ReadAddrSeq got null data at index {i}"); + return new(new Exception($"ReadAddrSeq got null data at index {i}")); + } + resultData.AddRange(data); + } + return resultData.ToArray(); + } + /// /// 向设备地址写入32位数据 /// diff --git a/src/APIClient.ts b/src/APIClient.ts index d8f25c1..3b49e6d 100644 --- a/src/APIClient.ts +++ b/src/APIClient.ts @@ -1126,23 +1126,13 @@ export class DataClient { /** * 新增板子(管理员权限) * @param name (optional) - * @param ipAddr (optional) - * @param port (optional) */ - addBoard(name: string | undefined, ipAddr: string | undefined, port: number | undefined): Promise { + addBoard(name: string | undefined): Promise { let url_ = this.baseUrl + "/api/Data/AddBoard?"; if (name === null) throw new Error("The parameter 'name' cannot be null."); else if (name !== undefined) url_ += "name=" + encodeURIComponent("" + name) + "&"; - if (ipAddr === null) - throw new Error("The parameter 'ipAddr' cannot be null."); - else if (ipAddr !== undefined) - url_ += "ipAddr=" + encodeURIComponent("" + ipAddr) + "&"; - if (port === null) - throw new Error("The parameter 'port' cannot be null."); - else if (port !== undefined) - url_ += "port=" + encodeURIComponent("" + port) + "&"; url_ = url_.replace(/[?&]$/, ""); let options_: RequestInit = { @@ -1157,7 +1147,7 @@ export class DataClient { }); } - protected processAddBoard(response: Response): Promise { + protected processAddBoard(response: Response): Promise { const status = response.status; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; if (status === 200) { @@ -1184,7 +1174,7 @@ export class DataClient { return throwException("An unexpected server error occurred.", status, _responseText, _headers); }); } - return Promise.resolve(null as any); + return Promise.resolve(null as any); } /** @@ -2689,6 +2679,269 @@ export class NetConfigClient { this.baseUrl = baseUrl ?? "http://localhost:5000"; } + /** + * 获取主机IP地址 + * @return 主机IP地址 + */ + getHostIP(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetHostIP"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetHostIP(_response); + }); + } + + protected processGetHostIP(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取板卡IP地址 + * @return 板卡IP地址 + */ + getBoardIP(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetBoardIP"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetBoardIP(_response); + }); + } + + protected processGetBoardIP(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取主机MAC地址 + * @return 主机MAC地址 + */ + getHostMAC(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetHostMAC"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetHostMAC(_response); + }); + } + + protected processGetHostMAC(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取板卡MAC地址 + * @return 板卡MAC地址 + */ + getBoardMAC(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetBoardMAC"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetBoardMAC(_response); + }); + } + + protected processGetBoardMAC(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取所有网络配置信息 + * @return 网络配置信息 + */ + getNetworkConfig(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetNetworkConfig"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetNetworkConfig(_response); + }); + } + + protected processGetNetworkConfig(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = NetworkConfigDto.fromJS(resultData200); + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + + /** + * 获取本机所有网络接口信息 + * @return 网络接口信息列表 + */ + getLocalNetworkInterfaces(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/GetLocalNetworkInterfaces"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "GET", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processGetLocalNetworkInterfaces(_response); + }); + } + + protected processGetLocalNetworkInterfaces(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + if (Array.isArray(resultData200)) { + result200 = [] as any; + for (let item of resultData200) + result200!.push(NetworkInterfaceDto.fromJS(item)); + } + else { + result200 = null; + } + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + /** * 设置主机IP地址 * @param hostIp (optional) 主机IP地址 @@ -2854,6 +3107,49 @@ export class NetConfigClient { return Promise.resolve(null as any); } + /** + * 自动获取本机IP地址并设置为实验板主机IP + * @return 操作结果 + */ + updateHostIP(): Promise { + let url_ = this.baseUrl + "/api/NetConfig/UpdateHostIP"; + url_ = url_.replace(/[?&]$/, ""); + + let options_: RequestInit = { + method: "POST", + headers: { + "Accept": "application/json" + } + }; + + return this.http.fetch(url_, options_).then((_response: Response) => { + return this.processUpdateHostIP(_response); + }); + } + + protected processUpdateHostIP(response: Response): Promise { + const status = response.status; + let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; + if (status === 200) { + return response.text().then((_responseText) => { + let result200: any = null; + let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); + result200 = resultData200 !== undefined ? resultData200 : null; + + return result200; + }); + } else if (status === 500) { + return response.text().then((_responseText) => { + return throwException("A server side error occurred.", status, _responseText, _headers); + }); + } else if (status !== 200 && status !== 204) { + return response.text().then((_responseText) => { + return throwException("An unexpected server error occurred.", status, _responseText, _headers); + }); + } + return Promise.resolve(null as any); + } + /** * 更新主机MAC地址 * @return 操作结果 @@ -4033,6 +4329,8 @@ export class Board implements IBoard { boardName!: string; /** FPGA 板子的IP地址 */ ipAddr!: string; + /** FPGA 板子的MAC地址 */ + macAddr!: string; /** FPGA 板子的通信端口 */ port!: number; /** FPGA 板子的当前状态 */ @@ -4058,6 +4356,7 @@ export class Board implements IBoard { this.id = _data["id"]; this.boardName = _data["boardName"]; this.ipAddr = _data["ipAddr"]; + this.macAddr = _data["macAddr"]; this.port = _data["port"]; this.status = _data["status"]; this.occupiedUserID = _data["occupiedUserID"]; @@ -4078,6 +4377,7 @@ export class Board implements IBoard { data["id"] = this.id; data["boardName"] = this.boardName; data["ipAddr"] = this.ipAddr; + data["macAddr"] = this.macAddr; data["port"] = this.port; data["status"] = this.status; data["occupiedUserID"] = this.occupiedUserID; @@ -4095,6 +4395,8 @@ export interface IBoard { boardName: string; /** FPGA 板子的IP地址 */ ipAddr: string; + /** FPGA 板子的MAC地址 */ + macAddr: string; /** FPGA 板子的通信端口 */ port: number; /** FPGA 板子的当前状态 */ @@ -4109,8 +4411,9 @@ export interface IBoard { /** FPGA 板子状态枚举 */ export enum BoardStatus { - Busy = 0, - Available = 1, + Disabled = 0, + Busy = 1, + Available = 2, } export class SystemException extends Exception implements ISystemException { @@ -4325,6 +4628,145 @@ export interface ISignalTriggerConfig { value: SignalValue; } +/** 网络配置数据传输对象 */ +export class NetworkConfigDto implements INetworkConfigDto { + /** 主机IP地址 */ + hostIP?: string | undefined; + /** 板卡IP地址 */ + boardIP?: string | undefined; + /** 主机MAC地址 */ + hostMAC?: string | undefined; + /** 板卡MAC地址 */ + boardMAC?: string | undefined; + + constructor(data?: INetworkConfigDto) { + if (data) { + for (var property in data) { + if (data.hasOwnProperty(property)) + (this)[property] = (data)[property]; + } + } + } + + init(_data?: any) { + if (_data) { + this.hostIP = _data["hostIP"]; + this.boardIP = _data["boardIP"]; + this.hostMAC = _data["hostMAC"]; + this.boardMAC = _data["boardMAC"]; + } + } + + static fromJS(data: any): NetworkConfigDto { + data = typeof data === 'object' ? data : {}; + let result = new NetworkConfigDto(); + result.init(data); + return result; + } + + toJSON(data?: any) { + data = typeof data === 'object' ? data : {}; + data["hostIP"] = this.hostIP; + data["boardIP"] = this.boardIP; + data["hostMAC"] = this.hostMAC; + data["boardMAC"] = this.boardMAC; + return data; + } +} + +/** 网络配置数据传输对象 */ +export interface INetworkConfigDto { + /** 主机IP地址 */ + hostIP?: string | undefined; + /** 板卡IP地址 */ + boardIP?: string | undefined; + /** 主机MAC地址 */ + hostMAC?: string | undefined; + /** 板卡MAC地址 */ + boardMAC?: string | undefined; +} + +/** 网络接口信息数据传输对象 */ +export class NetworkInterfaceDto implements INetworkInterfaceDto { + /** 网络接口名称 */ + name!: string; + /** 网络接口描述 */ + description!: string; + /** 网络接口类型 */ + type!: string; + /** 网络接口状态 */ + status!: string; + /** IP地址列表 */ + ipAddresses!: string[]; + /** MAC地址 */ + macAddress!: string; + + constructor(data?: INetworkInterfaceDto) { + if (data) { + for (var property in data) { + if (data.hasOwnProperty(property)) + (this)[property] = (data)[property]; + } + } + if (!data) { + this.ipAddresses = []; + } + } + + init(_data?: any) { + if (_data) { + this.name = _data["name"]; + this.description = _data["description"]; + this.type = _data["type"]; + this.status = _data["status"]; + if (Array.isArray(_data["ipAddresses"])) { + this.ipAddresses = [] as any; + for (let item of _data["ipAddresses"]) + this.ipAddresses!.push(item); + } + this.macAddress = _data["macAddress"]; + } + } + + static fromJS(data: any): NetworkInterfaceDto { + data = typeof data === 'object' ? data : {}; + let result = new NetworkInterfaceDto(); + result.init(data); + return result; + } + + toJSON(data?: any) { + data = typeof data === 'object' ? data : {}; + data["name"] = this.name; + data["description"] = this.description; + data["type"] = this.type; + data["status"] = this.status; + if (Array.isArray(this.ipAddresses)) { + data["ipAddresses"] = []; + for (let item of this.ipAddresses) + data["ipAddresses"].push(item); + } + data["macAddress"] = this.macAddress; + return data; + } +} + +/** 网络接口信息数据传输对象 */ +export interface INetworkInterfaceDto { + /** 网络接口名称 */ + name: string; + /** 网络接口描述 */ + description: string; + /** 网络接口类型 */ + type: string; + /** 网络接口状态 */ + status: string; + /** IP地址列表 */ + ipAddresses: string[]; + /** MAC地址 */ + macAddress: string; +} + /** Package options which to send address to read or write */ export class SendAddrPackOptions implements ISendAddrPackOptions { /** 突发类型 */