FPGA_WebLab/server/src/UdpClientPool.cs

486 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Net;
using System.Net.Sockets;
using System.Text;
using DotNext;
using WebProtocol;
/// <summary>
/// UDP客户端发送池
/// </summary>
public class UDPClientPool
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static IPAddress localhost = IPAddress.Parse("127.0.0.1");
/// <summary>
/// 发送字符串
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="stringArray">字符串数组</param>
/// <returns>是否成功</returns>
public static bool SendString(IPEndPoint endPoint, string[] stringArray)
{
var isSuccessful = true;
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
foreach (var str in stringArray)
{
byte[] sendbuf = Encoding.ASCII.GetBytes(str);
var sendLen = socket.SendTo(sendbuf, endPoint);
if (str.Length != sendLen) isSuccessful = false;
}
socket.Close();
if (isSuccessful) { return true; }
else { return false; }
}
/// <summary>
/// 异步发送字符串
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="stringArray">字符串数组</param>
/// <returns>是否成功</returns>
public async static ValueTask<bool> SendStringAsync(IPEndPoint endPoint, string[] stringArray)
{
return await Task.Run(() => { return SendString(endPoint, stringArray); });
}
/// <summary>
/// 发送二进制字节
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="buf">二进制字节</param>
/// <returns>是否成功</returns>
public static bool SendBytes(IPEndPoint endPoint, byte[] buf)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var sendLen = socket.SendTo(buf, endPoint);
socket.Close();
logger.Debug($"UDP socket send bytes to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(buf).Replace("-", " ")}");
if (sendLen == buf.Length) { return true; }
else { return false; }
}
/// <summary>
/// 异步发送二进制字节
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="buf">二进制字节</param>
/// <returns>是否成功</returns>
public async static ValueTask<bool> SendBytesAsync(IPEndPoint endPoint, byte[] buf)
{
return await Task.Run(() => { return SendBytes(endPoint, buf); });
}
/// <summary>
/// 发送地址包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkg">地址包</param>
/// <returns>是否成功</returns>
public static bool SendAddrPack(IPEndPoint endPoint, WebProtocol.SendAddrPackage pkg)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var sendBytes = pkg.ToBytes();
var sendLen = socket.SendTo(sendBytes, endPoint);
socket.Close();
logger.Debug($"UDP socket send address package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
logger.Debug($" Decoded Data: {pkg.ToString()}");
if (sendLen == sendBytes.Length) { return true; }
else { return false; }
}
/// <summary>
/// 异步发送地址包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkg">地址包</param>
/// <returns>是否成功</returns>
public async static ValueTask<bool> SendAddrPackAsync(IPEndPoint endPoint, WebProtocol.SendAddrPackage pkg)
{
return await Task.Run(() => { return SendAddrPack(endPoint, pkg); });
}
/// <summary>
/// 发送数据包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkg">数据包</param>
/// <returns>是否成功</returns>
public static bool SendDataPack(IPEndPoint endPoint, WebProtocol.SendDataPackage pkg)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
var sendBytes = pkg.ToBytes();
var sendLen = socket.SendTo(sendBytes, endPoint);
socket.Close();
logger.Debug($"UDP socket send data package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
if (sendLen == sendBytes.Length) { return true; }
else { return false; }
}
/// <summary>
/// 异步发送数据包
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="pkg">数据包</param>
/// <returns>是否成功</returns>
public async static ValueTask<bool> SendDataPackAsync(IPEndPoint endPoint, WebProtocol.SendDataPackage pkg)
{
return await Task.Run(() => { return SendDataPack(endPoint, pkg); });
}
/// <summary>
/// 发送字符串到本地
/// </summary>
/// <param name="port">端口</param>
/// <param name="stringArray">字符串数组</param>
/// <returns>是否成功</returns>
public static bool SendStringLocalHost(int port, string[] stringArray)
{
return SendString(new IPEndPoint(localhost, port), stringArray);
}
/// <summary>
/// 循环发送字符串到本地
/// </summary>
/// <param name="times">发送总次数</param>
/// <param name="sleepMilliSeconds">间隔时间</param>
/// <param name="port">端口</param>
/// <param name="stringArray">字符串数组</param>
/// <returns>是否成功</returns>
public static bool CycleSendStringLocalHost(int times, int sleepMilliSeconds, int port, string[] stringArray)
{
var isSuccessful = true;
while (times-- >= 0)
{
isSuccessful = SendStringLocalHost(port, stringArray);
if (!isSuccessful) break;
Thread.Sleep(sleepMilliSeconds);
}
return isSuccessful;
}
/// <summary>
/// 读取设备地址数据
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>读取结果,包含接收到的数据包</returns>
public static async ValueTask<Result<RecvDataPackage>> ReadAddr(
IPEndPoint endPoint, int taskID, uint devAddr, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.BurstLength = 0;
opts.CommandID = Convert.ToByte(taskID);
opts.Address = devAddr;
// Read Jtag State Register
opts.IsWrite = false;
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send Address Package Failed!"));
// Wait for Read Ack
if (!MsgBus.IsRunning)
return new(new Exception("Message Bus not Working!"));
var retPack = await MsgBus.UDPServer.WaitForDataAsync(
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
if (!retPack.IsSuccessful) return new(retPack.Error);
else if (!retPack.Value.IsSuccessful)
return new(new Exception("Send address package failed"));
var retPackOpts = retPack.Value.Options;
if (retPackOpts.Data is null)
return new(new Exception($"Data is Null, package: {retPackOpts.ToString()}"));
return retPack;
}
/// <summary>
/// 读取设备地址数据并校验结果
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="result">期望的结果值</param>
/// <param name="resultMask">结果掩码,用于位校验</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>校验结果true表示数据匹配期望值</returns>
public static async ValueTask<Result<bool>> ReadAddr(
IPEndPoint endPoint, int taskID, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
{
var address = endPoint.Address.ToString();
var ret = await ReadAddr(endPoint, taskID, devAddr, timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value.IsSuccessful)
return new(new Exception($"Read device {address} address {devAddr} failed"));
var retData = ret.Value.Options.Data;
if (retData is null)
return new(new Exception($"Device {address} receive none"));
if (retData.Length != 4)
return new(new Exception(
$"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
// Check result
try
{
var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
return Common.Number.BitsCheck(retCode, result, resultMask);
}
catch (Exception error)
{
return new(error);
}
}
/// <summary>
/// 读取设备地址数据并等待直到结果匹配或超时
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="result">期望的结果值</param>
/// <param name="resultMask">结果掩码,用于位校验</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>校验结果true表示在超时前数据匹配期望值</returns>
public static async ValueTask<Result<bool>> ReadAddrWithWait(
IPEndPoint endPoint, int taskID, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
{
var address = endPoint.Address.ToString();
var startTime = DateTime.Now;
while (true)
{
var elapsed = DateTime.Now - startTime;
if (elapsed >= TimeSpan.FromMilliseconds(timeout)) break;
var timeleft = TimeSpan.FromMilliseconds(timeout) - elapsed;
try
{
var ret = await ReadAddr(endPoint, taskID, devAddr, Convert.ToInt32(timeleft.TotalMilliseconds));
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value.IsSuccessful)
return new(new Exception($"Read device {address} address {devAddr} failed"));
var retData = ret.Value.Options.Data;
if (retData is null)
return new(new Exception($"Device {address} receive none"));
if (retData.Length != 4)
return new(new Exception(
$"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
// Check result
var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
if (Common.Number.BitsCheck(retCode, result, resultMask)) return true;
}
catch (Exception error)
{
return new(error);
}
}
return false;
}
/// <summary>
/// 从设备地址读取字节数组数据(支持大数据量分段传输)
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="dataLength">要读取的数据长度4字节</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>读取结果,包含接收到的字节数组</returns>
public static async ValueTask<Result<byte[]>> ReadAddr4Bytes(
IPEndPoint endPoint, int taskID, UInt32 devAddr, int dataLength, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
var resultData = new List<byte>();
opts.BurstType = BurstType.FixedBurst;
opts.CommandID = Convert.ToByte(taskID);
opts.IsWrite = false;
// Check Msg Bus
if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!"));
// Calculate read times and segments
var max4BytesPerRead = 0x80; // 512 bytes per read
var rest4Bytes = dataLength % max4BytesPerRead;
var readTimes = (rest4Bytes != 0) ?
(dataLength / max4BytesPerRead + 1) :
(dataLength / max4BytesPerRead);
for (var i = 0; i < readTimes; i++)
{
// Calculate current segment size
var isLastSegment = i == readTimes - 1;
var currentSegmentSize = (isLastSegment && rest4Bytes != 0) ? rest4Bytes : max4BytesPerRead;
// Set burst length (in 32-bit words)
opts.BurstLength = (byte)(currentSegmentSize - 1);
// Update address for current segment
opts.Address = devAddr + (uint)(i * max4BytesPerRead);
// Send read address package
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception($"Send address package failed at segment {i}!"));
// Wait for data response
var retPack = await MsgBus.UDPServer.WaitForDataAsync(
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
if (!retPack.IsSuccessful) return new(retPack.Error);
if (!retPack.Value.IsSuccessful)
return new(new Exception($"Read address package failed at segment {i}"));
var retPackOpts = retPack.Value.Options;
if (retPackOpts.Data is null)
return new(new Exception($"Data is null at segment {i}, package: {retPackOpts.ToString()}"));
// Validate received data length
if (retPackOpts.Data.Length != currentSegmentSize * 4)
return new(new Exception($"Expected {currentSegmentSize * 4} bytes but received {retPackOpts.Data.Length} bytes at segment {i}"));
// Add received data to result
resultData.AddRange(retPackOpts.Data);
}
// Validate total data length
if (resultData.Count != dataLength * 4)
return new(new Exception($"Expected total {dataLength * 4} bytes but received {resultData.Count} bytes"));
return resultData.ToArray();
}
/// <summary>
/// 向设备地址写入32位数据
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="data">要写入的32位数据</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>写入结果true表示写入成功</returns>
public static async ValueTask<Result<bool>> WriteAddr(
IPEndPoint endPoint, int taskID, UInt32 devAddr, UInt32 data, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.BurstLength = 0;
opts.CommandID = Convert.ToByte(taskID);
opts.Address = devAddr;
// Write Jtag State Register
opts.IsWrite = true;
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send 1st address package failed!"));
// Send Data Package
ret = await UDPClientPool.SendDataPackAsync(endPoint,
new SendDataPackage(Common.Number.NumberToBytes(data, 4).Value));
if (!ret) return new(new Exception("Send data package failed!"));
// Check Msg Bus
if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!"));
// Wait for Write Ack
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(
endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
return udpWriteAck.Value.IsSuccessful;
}
/// <summary>
/// 向设备地址写入字节数组数据(支持大数据量分段传输)
/// </summary>
/// <param name="endPoint">IP端点IP地址与端口</param>
/// <param name="taskID">任务ID</param>
/// <param name="devAddr">设备地址</param>
/// <param name="dataArray">要写入的字节数组</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>写入结果true表示写入成功</returns>
public static async ValueTask<Result<bool>> WriteAddr(
IPEndPoint endPoint, int taskID, UInt32 devAddr, byte[] dataArray, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.CommandID = Convert.ToByte(taskID);
opts.Address = devAddr;
// Check Msg Bus
if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!"));
opts.IsWrite = true;
var hasRest = dataArray.Length % (256 * (32 / 8)) != 0;
var writeTimes = hasRest ?
dataArray.Length / (256 * (32 / 8)) + 1 :
dataArray.Length / (256 * (32 / 8));
for (var i = 0; i < writeTimes; i++)
{
// Sperate Data Array
var isLastData = i == writeTimes - 1;
var sendDataArray = isLastData ?
dataArray[(i * (256 * (32 / 8)))..] :
dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)))];
// Calculate BurstLength
opts.BurstLength = ((byte)(
sendDataArray.Length % 4 == 0 ?
(sendDataArray.Length / 4 - 1) :
(sendDataArray.Length / 4)
));
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send 1st address package failed!"));
// Send Data Package
ret = await UDPClientPool.SendDataPackAsync(endPoint, new SendDataPackage(sendDataArray));
if (!ret) return new(new Exception("Send data package failed!"));
// Wait for Write Ack
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint.Address.ToString(), taskID, endPoint.Port, timeout);
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
if (!udpWriteAck.Value.IsSuccessful)
return false;
}
return true;
}
}