diff --git a/server/Program.cs b/server/Program.cs index f14bda2..2ec0f48 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -79,13 +79,16 @@ try // Setup Program MsgBus.Init(); + // if (app.Environment.IsDevelopment()) MsgBus.UDPServer.EnableDebugMode = true; + MsgBus.UDPServer.EnableDebugMode = true; // Router // API Get app.MapGet("/", () => Results.Redirect("/swagger")); app.MapGet("/api/GetRecvDataArray", Router.API.GetRecvDataArray); + // API Post app.MapPost("/api/SendString", Router.API.SendString); - // API Put + app.MapPost("/api/SendBytes", Router.API.SendBytes); app.MapPost("/api/SendAddrPackage", Router.API.SendAddrPackage); app.MapPost("/api/SendDataPackage", Router.API.SendDataPackage); // API Jtag diff --git a/server/src/Common.cs b/server/src/Common.cs index 465b096..066c3af 100644 --- a/server/src/Common.cs +++ b/server/src/Common.cs @@ -64,7 +64,7 @@ namespace Common return num; } - + public static Result MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len) { @@ -99,7 +99,7 @@ namespace Common } - public static Result StringToBytes(string str, int numBase = 16) + public static byte[] StringToBytes(string str, int numBase = 16) { var len = str.Length; var bytesLen = len / 2; diff --git a/server/src/Router.cs b/server/src/Router.cs index fe7bc03..f6f6569 100644 --- a/server/src/Router.cs +++ b/server/src/Router.cs @@ -39,19 +39,41 @@ namespace Router private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private const string LOCALHOST = "127.0.0.1"; + /// /// 发送字符串 /// - /// IPV4 或者 IPV6 地址 - /// 设备端口号 - /// Text for send - /// Json: true or false + /// IPV4 或者 IPV6 地址 + /// 设备端口号 + /// Text for send + [HttpPost("{address}/{port}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] public static async ValueTask SendString(string address, int port, string text) { var endPoint = new IPEndPoint(IPAddress.Parse(address), port); var ret = await UDPClientPool.SendStringAsync(endPoint, [text]); - if (ret) { return Results.Json(new Response() { IsSuccess = true }); } - else { return Results.Json(new Response() { IsSuccess = false }); } + + if (ret) { return TypedResults.Ok(); } + else { return TypedResults.InternalServerError(); } + } + + /// + /// 发送二进制数据 + /// + /// IPV4 或者 IPV6 地址 + /// 设备端口号 + /// 16进制文本 + [HttpPost("{address}/{port}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public static async ValueTask SendBytes(string address, int port, string bytes) + { + var endPoint = new IPEndPoint(IPAddress.Parse(address), port); + var ret = await UDPClientPool.SendBytesAsync(endPoint, NumberProcessor.StringToBytes(bytes)); + + if (ret) { return TypedResults.Ok(); } + else { return TypedResults.InternalServerError(); } } @@ -62,8 +84,8 @@ namespace Router /// UDP 端口号 /// 地址包选项 [HttpPost("{address}/{port}")] - [ProducesResponseType(200)] - [ProducesResponseType(500)] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] public static async ValueTask SendAddrPackage( string address, int port, @@ -71,20 +93,27 @@ namespace Router { var endPoint = new IPEndPoint(IPAddress.Parse(address), port); var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new WebProtocol.SendAddrPackage(opts)); + if (ret) { return TypedResults.Ok(); } else { return TypedResults.InternalServerError(); } } + [HttpPost("{address}/{port}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] public static async ValueTask SendDataPackage(string address, int port, string data) { var endPoint = new IPEndPoint(IPAddress.Parse(address), port); var ret = await UDPClientPool.SendDataPackAsync(endPoint, - new WebProtocol.SendDataPackage(NumberProcessor.StringToBytes(data).Value)); - if (ret) { return Results.Json(new Response() { IsSuccess = true }); } - else { return Results.Json(new Response() { IsSuccess = false }); } + new WebProtocol.SendDataPackage(NumberProcessor.StringToBytes(data))); + + if (ret) { return TypedResults.Ok(); } + else { return TypedResults.InternalServerError(); } } [HttpGet("{address}")] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] public static async ValueTask GetRecvDataArray(string address) { var ret = await MsgBus.UDPServer.GetDataArrayAsync(address); @@ -94,26 +123,19 @@ namespace Router var dataJson = JsonConvert.SerializeObject(ret.Value); logger.Debug($"Get Receive Successfully: {dataJson}"); - return TypedResults.Ok(new Response - { - IsSuccess = true, - Data = ret.Value, - }); + return TypedResults.Ok(ret.Value); } else { logger.Debug("Get Receive Failed"); - return TypedResults.Json(new Response - { - IsSuccess = false, - Data = "" - }); + return TypedResults.InternalServerError(); } } public class Jtag { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + /// /// 执行一个Jtag命令 /// @@ -121,16 +143,16 @@ namespace Router /// 设备端口 /// 16进制设备目的地址(Jtag) /// 16进制命令 - /// 16进制预期结果 - /// Response - public static async ValueTask RunCommand(string address, int port, string hexDevAddr, string hexCmd, string hexExRet) + [HttpPost] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public static async ValueTask RunCommand(string address, int port, string hexDevAddr, string hexCmd) { var jtagCtrl = new JtagController.Jtag(address, port); - var ret = await jtagCtrl.RunCommand(Convert.ToUInt32(hexDevAddr, 16), Convert.ToUInt32(hexCmd, 16), Convert.ToUInt32(hexExRet, 16)); + var ret = await jtagCtrl.RunCommand(Convert.ToUInt32(hexDevAddr, 16), Convert.ToUInt32(hexCmd, 16)); - - if (ret.IsSuccessful) { return Results.Json(new Response() { IsSuccess = true }); } - else { return Results.Json(new Response() { IsSuccess = false, Data = ret.Error }); } + if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); } + else { return TypedResults.InternalServerError(ret.Error); } } @@ -139,14 +161,16 @@ namespace Router /// /// 设备地址 /// 设备端口 - /// Response + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] public static async ValueTask GetDeviceIDCode(string address, int port) { var jtagCtrl = new JtagController.Jtag(address, port); var ret = await jtagCtrl.ReadIDCode(); - if (ret.IsSuccessful) { return Results.Json(new Response() { IsSuccess = true, Data = ret.Value }); } - else { return Results.Json(new Response() { IsSuccess = false, Data = ret.Error }); } + if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); } + else { return TypedResults.InternalServerError(ret.Error); } } diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs index 6c09b90..44fe26b 100644 --- a/server/src/UdpServer.cs +++ b/server/src/UdpServer.cs @@ -1,6 +1,7 @@ using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading.Tasks; using DotNext; using DotNext.Threading; using Newtonsoft.Json; @@ -42,6 +43,9 @@ public class UDPData /// /// UDP Server /// +/// +/// UDP 服务器 +/// public class UDPServer { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); @@ -52,6 +56,11 @@ public class UDPServer private Dictionary> udpData = new Dictionary>(); private AsyncReaderWriterLock udpDataLock = new AsyncReaderWriterLock(1); + /// + /// 是否开启Debug模式 + /// + public bool EnableDebugMode { get; set; } + /// /// Construct a udp server with fixed port /// @@ -256,52 +265,133 @@ public class UDPServer { var remoteEP = new IPEndPoint(IPAddress.Any, listenPort); byte[] bytes = listener.EndReceive(res, ref remoteEP); - var nowtime = DateTime.Now; // Handle RemoteEP - string remoteStr = "Unknown"; - using (udpDataLock.AcquireWriteLock()) + if (remoteEP is null) { - if (remoteEP is not null) - { - var remoteAddress = remoteEP.Address.ToString(); - var remotePort = remoteEP.Port; - // Record UDP Receive Data - if (udpData.ContainsKey(remoteAddress)) - { - var listData = udpData[remoteAddress]; - listData.Add(new UDPData() - { - Address = remoteAddress, - Port = remotePort, - Data = bytes, - DateTime = nowtime, - HasRead = false, - }); - logger.Trace("Receive data from old client"); - } - else - { - udpData.Add(remoteAddress, new List([new UDPData() - { - Address = remoteAddress, - Port = remotePort, - Data = bytes, - DateTime = nowtime, - HasRead = false, - }])); - logger.Trace("Receive data from new client"); - } + logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:"); + logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}"); + goto BEGIN_RECEIVE; + } - remoteStr = $"{remoteAddress}:{remotePort}"; + + // If enable Debug mode, exec Debug handler + if (EnableDebugMode) + { + DebugHandler(new IPEndPoint(IPAddress.Parse("127.0.0.1"), this.listenPort), bytes); + } + else + { + // Handle Package + PrintData(RecordUDPData(bytes, remoteEP)); + } + + BEGIN_RECEIVE: + listener.BeginReceive(new AsyncCallback(ReceiveHandler), null); + } + + private void DebugHandler(IPEndPoint ep, byte[] bytes) + { + // Print Receive Data + var data = new UDPData() + { + Address = ep.Address.ToString(), + Port = ep.Port, + Data = bytes, + DateTime = DateTime.Now, + HasRead = false, + }; + PrintData(data); + + // Handle Pack Type + var sign = bytes[0]; + if (sign == (byte)WebProtocol.PackSign.SendAddr) + { + var resData = WebProtocol.SendAddrPackage.FromBytes(bytes); + if (!resData.IsSuccessful) + { + logger.Warn("DebugHandler: Convert to SendAddrPackage failed"); + return; + } + + if (resData.Value.Options.IsWrite) + { + var pack = new WebProtocol.RecvRespPackage(resData.Value.Options.CommandID, true); + SendBytes(ep, pack.ToBytes()); } else { - logger.Warn("Receive data from unknown client"); + var pack = new WebProtocol.RecvDataPackage(resData.Value.Options.CommandID, true, [0, 0, 0, 0]); + SendBytes(ep, pack.ToBytes()); } } + else if (sign == (byte)WebProtocol.PackSign.SendData) + { + } + else + { + SendString(ep, "DebugHandler: Receive Data"); + } - // Handle Package + logger.Trace("DebugHandler: send pack successfully"); + } + + private bool SendBytes(IPEndPoint endPoint, byte[] buf) + { + var sendLen = listener.Send(buf, endPoint); + + if (sendLen == buf.Length) { return true; } + else { return false; } + } + + private bool SendString(IPEndPoint endPoint, string text) + { + byte[] buf = Encoding.ASCII.GetBytes(text); + var sendLen = listener.Send(buf, endPoint); + + if (sendLen == buf.Length) { return true; } + else { return false; } + } + + private UDPData RecordUDPData(byte[] bytes, IPEndPoint remoteEP) + { + using (udpDataLock.AcquireWriteLock()) + { + var remoteAddress = remoteEP.Address.ToString(); + var remotePort = remoteEP.Port; + var data = new UDPData() + { + Address = remoteAddress, + Port = remotePort, + Data = bytes, + DateTime = DateTime.Now, + HasRead = false, + }; + + // Record UDP Receive Data + if (udpData.ContainsKey(remoteAddress)) + { + var listData = udpData[remoteAddress]; + listData.Add(data); + logger.Trace("Receive data from old client"); + } + else + { + udpData.Add(remoteAddress, new List([data])); + logger.Trace("Receive data from new client"); + } + + return data; + } + } + + /// + /// 输出UDP Data到log中 + /// + /// UDP数据 + public void PrintData(UDPData data) + { + var bytes = data.Data; var sign = bytes[0]; string recvData = ""; if (sign == (byte)WebProtocol.PackSign.SendAddr) @@ -334,20 +424,16 @@ public class UDPServer recvData = Encoding.ASCII.GetString(bytes, 0, bytes.Length); } - logger.Debug($"Receive Data from {remoteStr} at {nowtime.ToString()}:"); - logger.Debug($"Original Data: {BitConverter.ToString(bytes).Replace("-", " ")}"); - if (recvData.Length != 0) logger.Debug(recvData); - // RecordAllData(); - - - listener.BeginReceive(new AsyncCallback(ReceiveHandler), null); + logger.Debug($"Receive Data from {data.Address}:{data.Port} at {data.DateTime.ToString()}:"); + logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}"); + if (recvData.Length != 0) logger.Debug($" Decoded Data : {recvData}"); } /// /// 将所有数据输出到log中 /// /// void - public void RecordAllData() + public void PrintAllData() { using (udpDataLock.AcquireReadLock()) { diff --git a/server/src/WebProtocol.cs b/server/src/WebProtocol.cs index 7401837..8bce0fe 100644 --- a/server/src/WebProtocol.cs +++ b/server/src/WebProtocol.cs @@ -172,6 +172,24 @@ namespace WebProtocol this.address = address; } + /// + /// 获取对应地址包选项 + /// + public SendAddrPackOptions Options + { + get + { + return new SendAddrPackOptions() + { + Address = this.address, + BurstLength = this.burstLength, + BurstType = (BurstType)(this.commandType >> 6), + CommandID = Convert.ToByte((this.commandType >> 4) & 0b11), + IsWrite = Convert.ToBoolean(this.commandType & 1) + }; + } + } + /// /// 将地址包转化为字节数组 /// @@ -298,6 +316,31 @@ namespace WebProtocol _ = this._reserved; } + /// + /// FPGA->Server 读响应包 + /// 构造函数 + /// + /// 任务ID号 + /// 是否读取成功 + /// 数据 + public RecvDataPackage(byte commandID, bool isSuccess, byte[] bodyData) + { + this.commandID = commandID; + this.resp = Convert.ToByte(isSuccess); + this.bodyData = bodyData; + } + + /// + /// 通过接受包选项构建读响应包 + /// + /// 接收包(读响应包和写响应包)选项 + public RecvDataPackage(RecvPackOptions opts) + { + this.commandID = opts.CommandID; + this.resp = Convert.ToByte(opts.IsSuccess ? 0b10 : 0b00); + this.bodyData = opts.Data ?? (byte[])[0, 0, 0, 0]; + } + /// /// 获取读响应包选项 /// @@ -337,6 +380,25 @@ namespace WebProtocol ); return new RecvDataPackage(bytes[1], bytes[2], bytes[4..]); } + + /// + /// 将数据包转化为字节数组 + /// + /// 字节数组 + public byte[] ToBytes() + { + var bodyDataLen = bodyData.Length; + var arr = new byte[4 + bodyDataLen]; + + arr[0] = this.sign; + arr[1] = this.commandID; + arr[2] = this.resp; + + Array.Copy(bodyData, 0, arr, 4, bodyDataLen); + + return arr; + } + } /// 写响应包 @@ -361,6 +423,27 @@ namespace WebProtocol _ = this._reserved; } + /// + /// 构建写响应包 + /// + /// 任务ID + /// 是否写成功 + public RecvRespPackage(byte commandID, bool isSuccess) + { + this.commandID = commandID; + this.resp = Convert.ToByte(isSuccess); + } + + /// + /// 通过接受包选项构建写响应包 + /// + /// 接收包(读响应包和写响应包)选项 + public RecvRespPackage(RecvPackOptions opts) + { + this.commandID = opts.CommandID; + this.resp = Convert.ToByte(opts.IsSuccess ? 0b10 : 0b00); + } + /// /// 获取写响应包选项 /// @@ -400,5 +483,20 @@ namespace WebProtocol ); return new RecvRespPackage(bytes[1], bytes[2]); } + + /// + /// 将数据包转化为字节数组 + /// + /// 字节数组 + public byte[] ToBytes() + { + var arr = new byte[4]; + + arr[0] = this.sign; + arr[1] = this.commandID; + arr[2] = this.resp; + + return arr; + } } }