feag: backend add matrix key peripheral with its web api
This commit is contained in:
		@@ -24,6 +24,9 @@ update:
 | 
			
		||||
gen-api:
 | 
			
		||||
  npm run gen-api
 | 
			
		||||
 | 
			
		||||
gen-api-from-server:
 | 
			
		||||
  npx nswag openapi2tsclient /input:http://localhost:5000/swagger/v1/swagger.json /output:src/APIClient.ts
 | 
			
		||||
 | 
			
		||||
# 构建服务器,包含win与linux平台
 | 
			
		||||
[working-directory: "server"]
 | 
			
		||||
build-server self-contained=isSelfContained: _show-dir
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using DotNext;
 | 
			
		||||
 | 
			
		||||
namespace Common
 | 
			
		||||
@@ -273,6 +274,21 @@ namespace Common
 | 
			
		||||
            return ((srcBits >> location) & ((UInt32)0b1)) == 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 将BitArray转化为32bits无符号整型
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="bits">BitArray比特数组</param>
 | 
			
		||||
        /// <returns>32bits无符号整型</returns>
 | 
			
		||||
        public static Result<UInt32> BitsToNumber(BitArray bits)
 | 
			
		||||
        {
 | 
			
		||||
            if (bits.Length > 32)
 | 
			
		||||
                throw new ArgumentException("Argument length shall be at most 32 bits.");
 | 
			
		||||
 | 
			
		||||
            var array = new UInt32[1];
 | 
			
		||||
            bits.CopyTo(array, 0);
 | 
			
		||||
            return array[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 字符串转二进制字节数组
 | 
			
		||||
 
 | 
			
		||||
@@ -1,891 +0,0 @@
 | 
			
		||||
using System.Net;
 | 
			
		||||
using Common;
 | 
			
		||||
using DotNext;
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using WebProtocol;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// UDP API 
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class UDPController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string LOCALHOST = "127.0.0.1";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 页面
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    public string Index()
 | 
			
		||||
    {
 | 
			
		||||
        return "This is UDP Controller";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送字符串
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IPV4 或者 IPV6 地址</param>
 | 
			
		||||
    /// <param name="port">设备端口号</param>
 | 
			
		||||
    /// <param name="text">发送的文本</param>
 | 
			
		||||
    /// <response code="200">发送成功</response>
 | 
			
		||||
    /// <response code="500">发送失败</response>
 | 
			
		||||
    [HttpPost("SendString")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendString(string address = LOCALHOST, int port = 1234, string text = "Hello Server!")
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendStringAsync(endPoint, [text]);
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送二进制数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address" example="127.0.0.1">IPV4 或者 IPV6 地址</param>
 | 
			
		||||
    /// <param name="port" example="1234">设备端口号</param>
 | 
			
		||||
    /// <param name="bytes" example="FFFFAAAA">16进制文本</param>
 | 
			
		||||
    [HttpPost("SendBytes")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendBytes(string address, int port, string bytes)
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendBytesAsync(endPoint, Number.StringToBytes(bytes));
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送地址包
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    /// <param name="port">UDP 端口号</param>
 | 
			
		||||
    /// <param name="opts">地址包选项</param>
 | 
			
		||||
    [HttpPost("SendAddrPackage")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendAddrPackage(
 | 
			
		||||
        string address,
 | 
			
		||||
        int port,
 | 
			
		||||
        [FromBody] SendAddrPackOptions opts)
 | 
			
		||||
    {
 | 
			
		||||
        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(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送数据包
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    /// <param name="port">UDP 端口号</param>
 | 
			
		||||
    /// <param name="data">16进制数据</param>
 | 
			
		||||
    [HttpPost("SendDataPackage")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendDataPackage(string address, int port, string data)
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendDataPackAsync(endPoint,
 | 
			
		||||
            new WebProtocol.SendDataPackage(Number.StringToBytes(data)));
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取指定IP地址接受的数据列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    [HttpGet("GetRecvDataArray")]
 | 
			
		||||
    [ProducesResponseType(typeof(List<UDPData>), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetRecvDataArray(string address)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await MsgBus.UDPServer.GetDataArrayAsync(address);
 | 
			
		||||
 | 
			
		||||
        if (ret.HasValue)
 | 
			
		||||
        {
 | 
			
		||||
            var dataJson = JsonConvert.SerializeObject(ret.Value);
 | 
			
		||||
            logger.Debug($"Get Receive Successfully: {dataJson}");
 | 
			
		||||
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Debug("Get Receive Failed");
 | 
			
		||||
            return TypedResults.InternalServerError();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Jtag API
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class JtagController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string BITSTREAM_PATH = "bitstream/Jtag";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 页面
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    public string Index()
 | 
			
		||||
    {
 | 
			
		||||
        return "This is Jtag Controller";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取Jtag ID Code
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpGet("GetDeviceIDCode")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetDeviceIDCode(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.ReadIDCode();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Get device {address} ID code: 0x{ret.Value:X4}");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取状态寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpGet("ReadStatusReg")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> ReadStatusReg(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.ReadStatusReg();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0'));
 | 
			
		||||
            var decodeValue = new JtagClient.JtagStatusReg(ret.Value);
 | 
			
		||||
            logger.Info($"Read device {address} Status Register: \n\t 0b{binaryValue} \n\t {decodeValue}");
 | 
			
		||||
            return TypedResults.Ok(new
 | 
			
		||||
            {
 | 
			
		||||
                original = ret.Value,
 | 
			
		||||
                binaryValue,
 | 
			
		||||
                decodeValue,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 上传比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="file">比特流文件</param>
 | 
			
		||||
    [HttpPost("UploadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    public async ValueTask<IResult> UploadBitstream(string address, IFormFile file)
 | 
			
		||||
    {
 | 
			
		||||
        if (file == null || file.Length == 0)
 | 
			
		||||
            return TypedResults.BadRequest("未选择文件");
 | 
			
		||||
 | 
			
		||||
        // 生成安全的文件名(避免路径遍历攻击)
 | 
			
		||||
        var fileName = Path.GetRandomFileName();
 | 
			
		||||
        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
 | 
			
		||||
        // 如果存在文件,则删除原文件再上传
 | 
			
		||||
        if (Directory.Exists(uploadsFolder))
 | 
			
		||||
        {
 | 
			
		||||
            Directory.Delete(uploadsFolder, true);
 | 
			
		||||
        }
 | 
			
		||||
        Directory.CreateDirectory(uploadsFolder);
 | 
			
		||||
 | 
			
		||||
        var filePath = Path.Combine(uploadsFolder, fileName);
 | 
			
		||||
 | 
			
		||||
        using (var stream = new FileStream(filePath, FileMode.Create))
 | 
			
		||||
        {
 | 
			
		||||
            await file.CopyToAsync(stream);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Info($"Device {address} Upload Bitstream Successfully");
 | 
			
		||||
        return TypedResults.Ok(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过Jtag下载比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpPost("DownloadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> DownloadBitstream(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
        if (!Directory.Exists(fileDir))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // 读取文件
 | 
			
		||||
            var filePath = Directory.GetFiles(fileDir)[0];
 | 
			
		||||
 | 
			
		||||
            using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
 | 
			
		||||
            {
 | 
			
		||||
                if (fileStream is null || fileStream.Length <= 0)
 | 
			
		||||
                    return TypedResults.BadRequest("Wrong bitstream, Please upload it again");
 | 
			
		||||
 | 
			
		||||
                // 定义缓冲区大小: 32KB
 | 
			
		||||
                byte[] buffer = new byte[32 * 1024];
 | 
			
		||||
                byte[] revBuffer = new byte[32 * 1024];
 | 
			
		||||
                long totalBytesRead = 0;
 | 
			
		||||
 | 
			
		||||
                // 使用异步流读取文件
 | 
			
		||||
                using (var memoryStream = new MemoryStream())
 | 
			
		||||
                {
 | 
			
		||||
                    int bytesRead;
 | 
			
		||||
                    while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        // 反转 32bits
 | 
			
		||||
                        var retBuffer = Common.Number.ReverseBytes(buffer, 4);
 | 
			
		||||
                        if (!retBuffer.IsSuccessful)
 | 
			
		||||
                            return TypedResults.InternalServerError(retBuffer.Error);
 | 
			
		||||
                        revBuffer = retBuffer.Value;
 | 
			
		||||
 | 
			
		||||
                        for (int i = 0; i < revBuffer.Length; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            revBuffer[i] = Common.Number.ReverseBits(revBuffer[i]);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
 | 
			
		||||
                        totalBytesRead += bytesRead;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
 | 
			
		||||
                    var fileBytes = memoryStream.ToArray();
 | 
			
		||||
 | 
			
		||||
                    // 下载比特流
 | 
			
		||||
                    var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
                    var ret = await jtagCtrl.DownloadBitstream(fileBytes);
 | 
			
		||||
 | 
			
		||||
                    if (ret.IsSuccessful)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Info($"Device {address} dowload bitstream successfully");
 | 
			
		||||
                        return TypedResults.Ok(ret.Value);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Error(ret.Error);
 | 
			
		||||
                        return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("BoundaryScanAllPorts")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> BoundaryScanAllPorts(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.BoundaryScan();
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("BoundaryScanLogicalPorts")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(Dictionary<string, bool>), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> BoundaryScanLogicalPorts(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.BoundaryScanLogicalPorts();
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="speed">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetSpeed")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetSpeed(string address, int port, UInt32 speed)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.SetSpeed(speed);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 远程更新
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class RemoteUpdateController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string BITSTREAM_PATH = "bitstream/RemoteUpdate";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 上传远程更新比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="goldenBitream">黄金比特流文件</param>
 | 
			
		||||
    /// <param name="bitstream1">比特流文件1</param>
 | 
			
		||||
    /// <param name="bitstream2">比特流文件2</param>
 | 
			
		||||
    /// <param name="bitstream3">比特流文件3</param>
 | 
			
		||||
    /// <returns>上传结果</returns>
 | 
			
		||||
    [HttpPost("UploadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    public async ValueTask<IResult> UploadBitstreams(
 | 
			
		||||
        string address,
 | 
			
		||||
        IFormFile? goldenBitream,
 | 
			
		||||
        IFormFile? bitstream1,
 | 
			
		||||
        IFormFile? bitstream2,
 | 
			
		||||
        IFormFile? bitstream3)
 | 
			
		||||
    {
 | 
			
		||||
        if ((goldenBitream is null || goldenBitream.Length == 0) &&
 | 
			
		||||
            (bitstream1 is null || bitstream1.Length == 0) &&
 | 
			
		||||
            (bitstream2 is null || bitstream2.Length == 0) &&
 | 
			
		||||
            (bitstream3 is null || bitstream3.Length == 0))
 | 
			
		||||
            return TypedResults.BadRequest("未选择文件");
 | 
			
		||||
 | 
			
		||||
        // 生成安全的文件名(避免路径遍历攻击)
 | 
			
		||||
        var fileName = Path.GetRandomFileName();
 | 
			
		||||
        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
 | 
			
		||||
        // 如果存在文件,则删除原文件再上传
 | 
			
		||||
        if (Directory.Exists(uploadsFolder))
 | 
			
		||||
        {
 | 
			
		||||
            Directory.Delete(uploadsFolder, true);
 | 
			
		||||
        }
 | 
			
		||||
        Directory.CreateDirectory(uploadsFolder);
 | 
			
		||||
 | 
			
		||||
        for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
 | 
			
		||||
        {
 | 
			
		||||
            IFormFile file;
 | 
			
		||||
            if (bitstreamNum == 0 && goldenBitream is not null)
 | 
			
		||||
                file = goldenBitream;
 | 
			
		||||
            else if (bitstreamNum == 1 && bitstream1 is not null)
 | 
			
		||||
                file = bitstream1;
 | 
			
		||||
            else if (bitstreamNum == 2 && bitstream2 is not null)
 | 
			
		||||
                file = bitstream2;
 | 
			
		||||
            else if (bitstreamNum == 3 && bitstream3 is not null)
 | 
			
		||||
                file = bitstream3;
 | 
			
		||||
            else continue;
 | 
			
		||||
 | 
			
		||||
            var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString());
 | 
			
		||||
            Directory.CreateDirectory(fileFolder);
 | 
			
		||||
 | 
			
		||||
            var filePath = Path.Combine(fileFolder, fileName);
 | 
			
		||||
 | 
			
		||||
            using (var stream = new FileStream(filePath, FileMode.Create))
 | 
			
		||||
            {
 | 
			
		||||
                await file.CopyToAsync(stream);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Info($"Device {address} Upload Bitstream Successfully");
 | 
			
		||||
        return TypedResults.Ok(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async ValueTask<Result<byte[]>> ProcessBitstream(string filePath)
 | 
			
		||||
    {
 | 
			
		||||
        using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
 | 
			
		||||
        {
 | 
			
		||||
            if (fileStream is null || fileStream.Length <= 0)
 | 
			
		||||
                return new(new ArgumentException("Wrong bitstream path"));
 | 
			
		||||
 | 
			
		||||
            // 定义缓冲区大小: 32KB
 | 
			
		||||
            byte[] buffer = new byte[32 * 1024];
 | 
			
		||||
            byte[] revBuffer = new byte[32 * 1024];
 | 
			
		||||
            long totalBytesRead = 0;
 | 
			
		||||
 | 
			
		||||
            // 使用异步流读取文件
 | 
			
		||||
            using (var memoryStream = new MemoryStream())
 | 
			
		||||
            {
 | 
			
		||||
                int bytesRead;
 | 
			
		||||
                while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // 反转 32bits
 | 
			
		||||
                    var retBuffer = Common.Number.ReverseBytes(buffer, 4);
 | 
			
		||||
                    if (!retBuffer.IsSuccessful)
 | 
			
		||||
                        return new(retBuffer.Error);
 | 
			
		||||
                    revBuffer = retBuffer.Value;
 | 
			
		||||
 | 
			
		||||
                    await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
 | 
			
		||||
                    totalBytesRead += bytesRead;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
 | 
			
		||||
                var restStreamLen = memoryStream.Length % (4 * 1024);
 | 
			
		||||
                if (restStreamLen != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    var appendLen = ((int)(4 * 1024 - restStreamLen));
 | 
			
		||||
                    var bytesAppend = new byte[appendLen];
 | 
			
		||||
                    Array.Fill<byte>(bytesAppend, 0xFF);
 | 
			
		||||
                    await memoryStream.WriteAsync(bytesAppend, 0, appendLen);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new(memoryStream.ToArray());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 远程更新单个比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    /// <param name="bitstreamNum"> 比特流位号 </param>
 | 
			
		||||
    [HttpPost("DownloadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> UpdateBitstream(string address, int port, int bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}");
 | 
			
		||||
        if (!Directory.Exists(fileDir))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // 读取文件
 | 
			
		||||
            var filePath = Directory.GetFiles(fileDir)[0];
 | 
			
		||||
 | 
			
		||||
            var fileBytes = await ProcessBitstream(filePath);
 | 
			
		||||
            if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
 | 
			
		||||
 | 
			
		||||
            // 下载比特流
 | 
			
		||||
            var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
            var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value);
 | 
			
		||||
 | 
			
		||||
            if (ret.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Info($"Device {address} Update bitstream successfully");
 | 
			
		||||
                return TypedResults.Ok(ret.Value);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error(ret.Error);
 | 
			
		||||
                return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载多个比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备地址</param>
 | 
			
		||||
    /// <param name="port">设备端口</param>
 | 
			
		||||
    /// <param name="bitstreamNum">比特流编号</param>
 | 
			
		||||
    /// <returns>总共上传比特流的数量</returns>
 | 
			
		||||
    [HttpPost("DownloadMultiBitstreams")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> DownloadMultiBitstreams(string address, int port, int? bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
        if (!Directory.Exists(bitstreamsFolder))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var bitstreams = new List<byte[]?>() { null, null, null, null };
 | 
			
		||||
            int cnt = 0; // 上传比特流数量
 | 
			
		||||
            for (int i = 0; i < 4; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString());
 | 
			
		||||
                if (!Directory.Exists(bitstreamDir))
 | 
			
		||||
                    continue;
 | 
			
		||||
                cnt++;
 | 
			
		||||
 | 
			
		||||
                // 读取文件
 | 
			
		||||
                var filePath = Directory.GetFiles(bitstreamDir)[0];
 | 
			
		||||
                var fileBytes = await ProcessBitstream(filePath);
 | 
			
		||||
                if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
 | 
			
		||||
                bitstreams[i] = fileBytes.Value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 下载比特流
 | 
			
		||||
            var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
            {
 | 
			
		||||
                var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]);
 | 
			
		||||
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (bitstreamNum is not null)
 | 
			
		||||
            {
 | 
			
		||||
                var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0);
 | 
			
		||||
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed");
 | 
			
		||||
            }
 | 
			
		||||
            return TypedResults.Ok(cnt);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 热复位比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备地址</param>
 | 
			
		||||
    /// <param name="port">设备端口</param>
 | 
			
		||||
    /// <param name="bitstreamNum">比特流编号</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("HotResetBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> HotResetBitstream(string address, int port, int bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
        var ret = await remoteUpdater.HotResetBitstream(bitstreamNum);
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} Update bitstream successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("GetFirmwareVersion")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(UInt32), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetFirmwareVersion(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
        var ret = await remoteUpdater.GetVersion();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} get firmware version successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// [TODO:description]
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class DDSController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetWaveNum")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetWaveNum(string address, int port, int channelNum, int waveNum)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetWaveNum(channelNum, waveNum);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output wave num successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="step">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetFreq")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetFreq(string address, int port, int channelNum, int waveNum, UInt32 step)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetFreq(channelNum, waveNum, step);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output freqency successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="phase">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetPhase")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetPhase(string address, int port, int channelNum, int waveNum, int phase)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetPhase(channelNum, waveNum, phase);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output phase successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// [TODO:description]
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class BsdlParserController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpGet("GetBoundaryLogicalPorts")]
 | 
			
		||||
    public IResult GetBoundaryLogicalPorts()
 | 
			
		||||
    {
 | 
			
		||||
        var parser = new BsdlParser.Parser();
 | 
			
		||||
        var ret = parser.GetBoundaryLogicalPorts();
 | 
			
		||||
        if (ret.IsNull) return TypedResults.InternalServerError("Get Null");
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 数据控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class DataController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 创建数据库表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpPost("CreateTable")]
 | 
			
		||||
    public IResult CreateTables()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        db.CreateAllTables();
 | 
			
		||||
        return TypedResults.Ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除数据库表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpDelete("DropTables")]
 | 
			
		||||
    public IResult DropTables()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        db.DropAllTables();
 | 
			
		||||
        return TypedResults.Ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取所有用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>用户列表</returns>
 | 
			
		||||
    [HttpGet("AllUsers")]
 | 
			
		||||
    public IResult AllUsers()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        var ret = db.User.ToList();
 | 
			
		||||
        return TypedResults.Ok(ret);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 注册新用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">用户名</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("SignUpUser")]
 | 
			
		||||
    public IResult SignUpUser(string name)
 | 
			
		||||
    {
 | 
			
		||||
        if (name.Length > 255)
 | 
			
		||||
            return TypedResults.BadRequest("Name Couln't over 255 characters");
 | 
			
		||||
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        var ret = db.AddUser(name);
 | 
			
		||||
        return TypedResults.Ok(ret);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								server/src/Controllers/BsdlParserController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								server/src/Controllers/BsdlParserController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// [TODO:description]
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class BsdlParserController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpGet("GetBoundaryLogicalPorts")]
 | 
			
		||||
    public IResult GetBoundaryLogicalPorts()
 | 
			
		||||
    {
 | 
			
		||||
        var parser = new BsdlParser.Parser();
 | 
			
		||||
        var ret = parser.GetBoundaryLogicalPorts();
 | 
			
		||||
        if (ret.IsNull) return TypedResults.InternalServerError("Get Null");
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										108
									
								
								server/src/Controllers/DDSController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								server/src/Controllers/DDSController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// [TODO:description]
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class DDSController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetWaveNum")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetWaveNum(string address, int port, int channelNum, int waveNum)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new Peripherals.DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetWaveNum(channelNum, waveNum);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output wave num successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="step">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetFreq")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetFreq(string address, int port, int channelNum, int waveNum, UInt32 step)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new Peripherals.DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetFreq(channelNum, waveNum, step);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output freqency successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="channelNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="waveNum">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="phase">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetPhase")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetPhase(string address, int port, int channelNum, int waveNum, int phase)
 | 
			
		||||
    {
 | 
			
		||||
        var dds = new Peripherals.DDSClient.DDS(address, port);
 | 
			
		||||
 | 
			
		||||
        var ret = await dds.SetPhase(channelNum, waveNum, phase);
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} set output phase successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										69
									
								
								server/src/Controllers/DataController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								server/src/Controllers/DataController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 数据控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class DataController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 创建数据库表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpPost("CreateTable")]
 | 
			
		||||
    public IResult CreateTables()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        db.CreateAllTables();
 | 
			
		||||
        return TypedResults.Ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除数据库表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpDelete("DropTables")]
 | 
			
		||||
    public IResult DropTables()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        db.DropAllTables();
 | 
			
		||||
        return TypedResults.Ok();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取所有用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>用户列表</returns>
 | 
			
		||||
    [HttpGet("AllUsers")]
 | 
			
		||||
    public IResult AllUsers()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        var ret = db.User.ToList();
 | 
			
		||||
        return TypedResults.Ok(ret);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 注册新用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">用户名</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("SignUpUser")]
 | 
			
		||||
    public IResult SignUpUser(string name)
 | 
			
		||||
    {
 | 
			
		||||
        if (name.Length > 255)
 | 
			
		||||
            return TypedResults.BadRequest("Name Couln't over 255 characters");
 | 
			
		||||
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        var ret = db.AddUser(name);
 | 
			
		||||
        return TypedResults.Ok(ret);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										278
									
								
								server/src/Controllers/JtagController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								server/src/Controllers/JtagController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Jtag API
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class JtagController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string BITSTREAM_PATH = "bitstream/Jtag";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 页面
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    public string Index()
 | 
			
		||||
    {
 | 
			
		||||
        return "This is Jtag Controller";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取Jtag ID Code
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpGet("GetDeviceIDCode")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetDeviceIDCode(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.ReadIDCode();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Get device {address} ID code: 0x{ret.Value:X4}");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取状态寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpGet("ReadStatusReg")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> ReadStatusReg(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.ReadStatusReg();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0'));
 | 
			
		||||
            var decodeValue = new JtagClient.JtagStatusReg(ret.Value);
 | 
			
		||||
            logger.Info($"Read device {address} Status Register: \n\t 0b{binaryValue} \n\t {decodeValue}");
 | 
			
		||||
            return TypedResults.Ok(new
 | 
			
		||||
            {
 | 
			
		||||
                original = ret.Value,
 | 
			
		||||
                binaryValue,
 | 
			
		||||
                decodeValue,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 上传比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="file">比特流文件</param>
 | 
			
		||||
    [HttpPost("UploadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    public async ValueTask<IResult> UploadBitstream(string address, IFormFile file)
 | 
			
		||||
    {
 | 
			
		||||
        if (file == null || file.Length == 0)
 | 
			
		||||
            return TypedResults.BadRequest("未选择文件");
 | 
			
		||||
 | 
			
		||||
        // 生成安全的文件名(避免路径遍历攻击)
 | 
			
		||||
        var fileName = Path.GetRandomFileName();
 | 
			
		||||
        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
 | 
			
		||||
        // 如果存在文件,则删除原文件再上传
 | 
			
		||||
        if (Directory.Exists(uploadsFolder))
 | 
			
		||||
        {
 | 
			
		||||
            Directory.Delete(uploadsFolder, true);
 | 
			
		||||
        }
 | 
			
		||||
        Directory.CreateDirectory(uploadsFolder);
 | 
			
		||||
 | 
			
		||||
        var filePath = Path.Combine(uploadsFolder, fileName);
 | 
			
		||||
 | 
			
		||||
        using (var stream = new FileStream(filePath, FileMode.Create))
 | 
			
		||||
        {
 | 
			
		||||
            await file.CopyToAsync(stream);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Info($"Device {address} Upload Bitstream Successfully");
 | 
			
		||||
        return TypedResults.Ok(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过Jtag下载比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    [HttpPost("DownloadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> DownloadBitstream(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
        if (!Directory.Exists(fileDir))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // 读取文件
 | 
			
		||||
            var filePath = Directory.GetFiles(fileDir)[0];
 | 
			
		||||
 | 
			
		||||
            using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
 | 
			
		||||
            {
 | 
			
		||||
                if (fileStream is null || fileStream.Length <= 0)
 | 
			
		||||
                    return TypedResults.BadRequest("Wrong bitstream, Please upload it again");
 | 
			
		||||
 | 
			
		||||
                // 定义缓冲区大小: 32KB
 | 
			
		||||
                byte[] buffer = new byte[32 * 1024];
 | 
			
		||||
                byte[] revBuffer = new byte[32 * 1024];
 | 
			
		||||
                long totalBytesRead = 0;
 | 
			
		||||
 | 
			
		||||
                // 使用异步流读取文件
 | 
			
		||||
                using (var memoryStream = new MemoryStream())
 | 
			
		||||
                {
 | 
			
		||||
                    int bytesRead;
 | 
			
		||||
                    while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        // 反转 32bits
 | 
			
		||||
                        var retBuffer = Common.Number.ReverseBytes(buffer, 4);
 | 
			
		||||
                        if (!retBuffer.IsSuccessful)
 | 
			
		||||
                            return TypedResults.InternalServerError(retBuffer.Error);
 | 
			
		||||
                        revBuffer = retBuffer.Value;
 | 
			
		||||
 | 
			
		||||
                        for (int i = 0; i < revBuffer.Length; i++)
 | 
			
		||||
                        {
 | 
			
		||||
                            revBuffer[i] = Common.Number.ReverseBits(revBuffer[i]);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
 | 
			
		||||
                        totalBytesRead += bytesRead;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
 | 
			
		||||
                    var fileBytes = memoryStream.ToArray();
 | 
			
		||||
 | 
			
		||||
                    // 下载比特流
 | 
			
		||||
                    var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
                    var ret = await jtagCtrl.DownloadBitstream(fileBytes);
 | 
			
		||||
 | 
			
		||||
                    if (ret.IsSuccessful)
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Info($"Device {address} dowload bitstream successfully");
 | 
			
		||||
                        return TypedResults.Ok(ret.Value);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        logger.Error(ret.Error);
 | 
			
		||||
                        return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("BoundaryScanAllPorts")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> BoundaryScanAllPorts(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.BoundaryScan();
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("BoundaryScanLogicalPorts")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(Dictionary<string, bool>), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> BoundaryScanLogicalPorts(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.BoundaryScanLogicalPorts();
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="speed">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("SetSpeed")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetSpeed(string address, int port, UInt32 speed)
 | 
			
		||||
    {
 | 
			
		||||
        var jtagCtrl = new JtagClient.Jtag(address, port);
 | 
			
		||||
        var ret = await jtagCtrl.SetSpeed(speed);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            if (ret.Error is ArgumentException)
 | 
			
		||||
                return TypedResults.BadRequest(ret.Error);
 | 
			
		||||
            else return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return TypedResults.Ok(ret.Value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								server/src/Controllers/MatrixKeyController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								server/src/Controllers/MatrixKeyController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 矩阵键控制器,用于管理矩阵键的启用、禁用和状态设置
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class MatrixKeyController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启用矩阵键控制。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备的IP地址</param>
 | 
			
		||||
    /// <param name="port">设备的端口号</param>
 | 
			
		||||
    /// <returns>返回操作结果的状态码</returns>
 | 
			
		||||
    [HttpGet("EnabelMatrixKey")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> EnabelMatrixKey(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var matrixKeyCtrl = new Peripherals.MatrixKeyClient.MatrixKey(address, port);
 | 
			
		||||
        var ret = await matrixKeyCtrl.EnableControl();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Enable device {address}:{port.ToString()} matrix key finished: {ret.Value}.");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 禁用矩阵键控制。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备的IP地址</param>
 | 
			
		||||
    /// <param name="port">设备的端口号</param>
 | 
			
		||||
    /// <returns>返回操作结果的状态码</returns>
 | 
			
		||||
    [HttpGet("DisableMatrixKey")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> DisableMatrixKey(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var matrixKeyCtrl = new Peripherals.MatrixKeyClient.MatrixKey(address, port);
 | 
			
		||||
        var ret = await matrixKeyCtrl.DisableControl();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Disable device {address}:{port.ToString()} matrix key finished: {ret.Value}.");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置矩阵键的状态。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备的IP地址</param>
 | 
			
		||||
    /// <param name="port">设备的端口号</param>
 | 
			
		||||
    /// <param name="keyStates">矩阵键的状态数组,长度应为16</param>
 | 
			
		||||
    /// <returns>返回操作结果的状态码</returns>
 | 
			
		||||
    [HttpGet("SetMatrixKeyStatus")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SetMatrixKeyStatus(string address, int port, [FromBody] bool[] keyStates)
 | 
			
		||||
    {
 | 
			
		||||
        if (keyStates.Length != 16)
 | 
			
		||||
            return TypedResults.BadRequest($"The length of key states should be 16 instead of {keyStates.Length}");
 | 
			
		||||
 | 
			
		||||
        var matrixKeyCtrl = new Peripherals.MatrixKeyClient.MatrixKey(address, port);
 | 
			
		||||
        var ret = await matrixKeyCtrl.ControlKey(new BitArray(keyStates));
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Set device {address}:{port.ToString()} matrix key finished: {ret.Value}.");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										292
									
								
								server/src/Controllers/RemoteUpdateController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								server/src/Controllers/RemoteUpdateController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,292 @@
 | 
			
		||||
using DotNext;
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 远程更新
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class RemoteUpdateController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string BITSTREAM_PATH = "bitstream/RemoteUpdate";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 上传远程更新比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="goldenBitream">黄金比特流文件</param>
 | 
			
		||||
    /// <param name="bitstream1">比特流文件1</param>
 | 
			
		||||
    /// <param name="bitstream2">比特流文件2</param>
 | 
			
		||||
    /// <param name="bitstream3">比特流文件3</param>
 | 
			
		||||
    /// <returns>上传结果</returns>
 | 
			
		||||
    [HttpPost("UploadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    public async ValueTask<IResult> UploadBitstreams(
 | 
			
		||||
        string address,
 | 
			
		||||
        IFormFile? goldenBitream,
 | 
			
		||||
        IFormFile? bitstream1,
 | 
			
		||||
        IFormFile? bitstream2,
 | 
			
		||||
        IFormFile? bitstream3)
 | 
			
		||||
    {
 | 
			
		||||
        if ((goldenBitream is null || goldenBitream.Length == 0) &&
 | 
			
		||||
            (bitstream1 is null || bitstream1.Length == 0) &&
 | 
			
		||||
            (bitstream2 is null || bitstream2.Length == 0) &&
 | 
			
		||||
            (bitstream3 is null || bitstream3.Length == 0))
 | 
			
		||||
            return TypedResults.BadRequest("未选择文件");
 | 
			
		||||
 | 
			
		||||
        // 生成安全的文件名(避免路径遍历攻击)
 | 
			
		||||
        var fileName = Path.GetRandomFileName();
 | 
			
		||||
        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
 | 
			
		||||
        // 如果存在文件,则删除原文件再上传
 | 
			
		||||
        if (Directory.Exists(uploadsFolder))
 | 
			
		||||
        {
 | 
			
		||||
            Directory.Delete(uploadsFolder, true);
 | 
			
		||||
        }
 | 
			
		||||
        Directory.CreateDirectory(uploadsFolder);
 | 
			
		||||
 | 
			
		||||
        for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
 | 
			
		||||
        {
 | 
			
		||||
            IFormFile file;
 | 
			
		||||
            if (bitstreamNum == 0 && goldenBitream is not null)
 | 
			
		||||
                file = goldenBitream;
 | 
			
		||||
            else if (bitstreamNum == 1 && bitstream1 is not null)
 | 
			
		||||
                file = bitstream1;
 | 
			
		||||
            else if (bitstreamNum == 2 && bitstream2 is not null)
 | 
			
		||||
                file = bitstream2;
 | 
			
		||||
            else if (bitstreamNum == 3 && bitstream3 is not null)
 | 
			
		||||
                file = bitstream3;
 | 
			
		||||
            else continue;
 | 
			
		||||
 | 
			
		||||
            var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString());
 | 
			
		||||
            Directory.CreateDirectory(fileFolder);
 | 
			
		||||
 | 
			
		||||
            var filePath = Path.Combine(fileFolder, fileName);
 | 
			
		||||
 | 
			
		||||
            using (var stream = new FileStream(filePath, FileMode.Create))
 | 
			
		||||
            {
 | 
			
		||||
                await file.CopyToAsync(stream);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        logger.Info($"Device {address} Upload Bitstream Successfully");
 | 
			
		||||
        return TypedResults.Ok(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async ValueTask<Result<byte[]>> ProcessBitstream(string filePath)
 | 
			
		||||
    {
 | 
			
		||||
        using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
 | 
			
		||||
        {
 | 
			
		||||
            if (fileStream is null || fileStream.Length <= 0)
 | 
			
		||||
                return new(new ArgumentException("Wrong bitstream path"));
 | 
			
		||||
 | 
			
		||||
            // 定义缓冲区大小: 32KB
 | 
			
		||||
            byte[] buffer = new byte[32 * 1024];
 | 
			
		||||
            byte[] revBuffer = new byte[32 * 1024];
 | 
			
		||||
            long totalBytesRead = 0;
 | 
			
		||||
 | 
			
		||||
            // 使用异步流读取文件
 | 
			
		||||
            using (var memoryStream = new MemoryStream())
 | 
			
		||||
            {
 | 
			
		||||
                int bytesRead;
 | 
			
		||||
                while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    // 反转 32bits
 | 
			
		||||
                    var retBuffer = Common.Number.ReverseBytes(buffer, 4);
 | 
			
		||||
                    if (!retBuffer.IsSuccessful)
 | 
			
		||||
                        return new(retBuffer.Error);
 | 
			
		||||
                    revBuffer = retBuffer.Value;
 | 
			
		||||
 | 
			
		||||
                    await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
 | 
			
		||||
                    totalBytesRead += bytesRead;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
 | 
			
		||||
                var restStreamLen = memoryStream.Length % (4 * 1024);
 | 
			
		||||
                if (restStreamLen != 0)
 | 
			
		||||
                {
 | 
			
		||||
                    var appendLen = ((int)(4 * 1024 - restStreamLen));
 | 
			
		||||
                    var bytesAppend = new byte[appendLen];
 | 
			
		||||
                    Array.Fill<byte>(bytesAppend, 0xFF);
 | 
			
		||||
                    await memoryStream.WriteAsync(bytesAppend, 0, appendLen);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new(memoryStream.ToArray());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 远程更新单个比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"> 设备地址 </param>
 | 
			
		||||
    /// <param name="port"> 设备端口 </param>
 | 
			
		||||
    /// <param name="bitstreamNum"> 比特流位号 </param>
 | 
			
		||||
    [HttpPost("DownloadBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> UpdateBitstream(string address, int port, int bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}");
 | 
			
		||||
        if (!Directory.Exists(fileDir))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            // 读取文件
 | 
			
		||||
            var filePath = Directory.GetFiles(fileDir)[0];
 | 
			
		||||
 | 
			
		||||
            var fileBytes = await ProcessBitstream(filePath);
 | 
			
		||||
            if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
 | 
			
		||||
 | 
			
		||||
            // 下载比特流
 | 
			
		||||
            var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
            var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value);
 | 
			
		||||
 | 
			
		||||
            if (ret.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Info($"Device {address} Update bitstream successfully");
 | 
			
		||||
                return TypedResults.Ok(ret.Value);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error(ret.Error);
 | 
			
		||||
                return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载多个比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备地址</param>
 | 
			
		||||
    /// <param name="port">设备端口</param>
 | 
			
		||||
    /// <param name="bitstreamNum">比特流编号</param>
 | 
			
		||||
    /// <returns>总共上传比特流的数量</returns>
 | 
			
		||||
    [HttpPost("DownloadMultiBitstreams")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> DownloadMultiBitstreams(string address, int port, int? bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        // 检查文件
 | 
			
		||||
        var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
 | 
			
		||||
        if (!Directory.Exists(bitstreamsFolder))
 | 
			
		||||
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var bitstreams = new List<byte[]?>() { null, null, null, null };
 | 
			
		||||
            int cnt = 0; // 上传比特流数量
 | 
			
		||||
            for (int i = 0; i < 4; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString());
 | 
			
		||||
                if (!Directory.Exists(bitstreamDir))
 | 
			
		||||
                    continue;
 | 
			
		||||
                cnt++;
 | 
			
		||||
 | 
			
		||||
                // 读取文件
 | 
			
		||||
                var filePath = Directory.GetFiles(bitstreamDir)[0];
 | 
			
		||||
                var fileBytes = await ProcessBitstream(filePath);
 | 
			
		||||
                if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
 | 
			
		||||
                bitstreams[i] = fileBytes.Value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 下载比特流
 | 
			
		||||
            var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
            {
 | 
			
		||||
                var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]);
 | 
			
		||||
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (bitstreamNum is not null)
 | 
			
		||||
            {
 | 
			
		||||
                var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0);
 | 
			
		||||
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
                if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed");
 | 
			
		||||
            }
 | 
			
		||||
            return TypedResults.Ok(cnt);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception error)
 | 
			
		||||
        {
 | 
			
		||||
            return TypedResults.InternalServerError(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 热复位比特流文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备地址</param>
 | 
			
		||||
    /// <param name="port">设备端口</param>
 | 
			
		||||
    /// <param name="bitstreamNum">比特流编号</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("HotResetBitstream")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> HotResetBitstream(string address, int port, int bitstreamNum)
 | 
			
		||||
    {
 | 
			
		||||
        var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
        var ret = await remoteUpdater.HotResetBitstream(bitstreamNum);
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} Update bitstream successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="port">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("GetFirmwareVersion")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(UInt32), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetFirmwareVersion(string address, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var remoteUpdater = new RemoteUpdateClient.RemoteUpdater(address, port);
 | 
			
		||||
        var ret = await remoteUpdater.GetVersion();
 | 
			
		||||
 | 
			
		||||
        if (ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Info($"Device {address} get firmware version successfully");
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ret.Error);
 | 
			
		||||
            return TypedResults.InternalServerError(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										133
									
								
								server/src/Controllers/UDPController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								server/src/Controllers/UDPController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
			
		||||
using System.Net;
 | 
			
		||||
using Common;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using WebProtocol;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// UDP API 
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class UDPController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    private const string LOCALHOST = "127.0.0.1";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 页面
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    public string Index()
 | 
			
		||||
    {
 | 
			
		||||
        return "This is UDP Controller";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送字符串
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IPV4 或者 IPV6 地址</param>
 | 
			
		||||
    /// <param name="port">设备端口号</param>
 | 
			
		||||
    /// <param name="text">发送的文本</param>
 | 
			
		||||
    /// <response code="200">发送成功</response>
 | 
			
		||||
    /// <response code="500">发送失败</response>
 | 
			
		||||
    [HttpPost("SendString")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendString(string address = LOCALHOST, int port = 1234, string text = "Hello Server!")
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendStringAsync(endPoint, [text]);
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送二进制数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address" example="127.0.0.1">IPV4 或者 IPV6 地址</param>
 | 
			
		||||
    /// <param name="port" example="1234">设备端口号</param>
 | 
			
		||||
    /// <param name="bytes" example="FFFFAAAA">16进制文本</param>
 | 
			
		||||
    [HttpPost("SendBytes")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendBytes(string address, int port, string bytes)
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendBytesAsync(endPoint, Number.StringToBytes(bytes));
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送地址包
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    /// <param name="port">UDP 端口号</param>
 | 
			
		||||
    /// <param name="opts">地址包选项</param>
 | 
			
		||||
    [HttpPost("SendAddrPackage")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendAddrPackage(
 | 
			
		||||
        string address,
 | 
			
		||||
        int port,
 | 
			
		||||
        [FromBody] SendAddrPackOptions opts)
 | 
			
		||||
    {
 | 
			
		||||
        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(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送数据包
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    /// <param name="port">UDP 端口号</param>
 | 
			
		||||
    /// <param name="data">16进制数据</param>
 | 
			
		||||
    [HttpPost("SendDataPackage")]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> SendDataPackage(string address, int port, string data)
 | 
			
		||||
    {
 | 
			
		||||
        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        var ret = await UDPClientPool.SendDataPackAsync(endPoint,
 | 
			
		||||
            new WebProtocol.SendDataPackage(Number.StringToBytes(data)));
 | 
			
		||||
 | 
			
		||||
        if (ret) { return TypedResults.Ok(); }
 | 
			
		||||
        else { return TypedResults.InternalServerError(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取指定IP地址接受的数据列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">IP地址</param>
 | 
			
		||||
    [HttpGet("GetRecvDataArray")]
 | 
			
		||||
    [ProducesResponseType(typeof(List<UDPData>), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    public async ValueTask<IResult> GetRecvDataArray(string address)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await MsgBus.UDPServer.GetDataArrayAsync(address);
 | 
			
		||||
 | 
			
		||||
        if (ret.HasValue)
 | 
			
		||||
        {
 | 
			
		||||
            var dataJson = JsonConvert.SerializeObject(ret.Value);
 | 
			
		||||
            logger.Debug($"Get Receive Successfully: {dataJson}");
 | 
			
		||||
 | 
			
		||||
            return TypedResults.Ok(ret.Value);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            logger.Debug("Get Receive Failed");
 | 
			
		||||
            return TypedResults.InternalServerError();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -776,9 +776,9 @@ public class Jtag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 边界扫描
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <returns>返回所有引脚边界扫描结果</returns>
 | 
			
		||||
    public async ValueTask<Result<BitArray>> BoundaryScan()
 | 
			
		||||
    {
 | 
			
		||||
        var paser = new BsdlParser.Parser();
 | 
			
		||||
@@ -824,9 +824,9 @@ public class Jtag
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 执行边界扫描并返回逻辑端口的状态
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <returns>包含逻辑端口状态的字典,键为端口ID,值为布尔状态</returns>
 | 
			
		||||
    public async ValueTask<Result<Dictionary<string, bool>>> BoundaryScanLogicalPorts()
 | 
			
		||||
    {
 | 
			
		||||
        var bitArray = await BoundaryScan();
 | 
			
		||||
@@ -846,22 +846,22 @@ public class Jtag
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// 设置JTAG的运行速度。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="speed">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    /// <param name="speed">运行速度值。</param>
 | 
			
		||||
    /// <returns>指示操作是否成功的异步结果。</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetSpeed(UInt32 speed)
 | 
			
		||||
    {
 | 
			
		||||
        // Clear Data
 | 
			
		||||
        await MsgBus.UDPServer.ClearUDPData(this.address);
 | 
			
		||||
 | 
			
		||||
        logger.Trace($"Clear up udp server {this.address} receive data");
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        var ret = await WriteFIFO(
 | 
			
		||||
            JtagAddr.SPEED_CTRL, (speed << 16) | speed,
 | 
			
		||||
            JtagState.CMD_EXEC_FINISH, JtagState.CMD_EXEC_FINISH);
 | 
			
		||||
        
 | 
			
		||||
        if (!ret.IsSuccessful) return new (ret.Error);
 | 
			
		||||
 | 
			
		||||
        if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
        return ret.Value;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
using System.Net;
 | 
			
		||||
using DotNext;
 | 
			
		||||
 | 
			
		||||
namespace DDSClient;
 | 
			
		||||
namespace Peripherals.DDSClient;
 | 
			
		||||
 | 
			
		||||
static class DDSAddr
 | 
			
		||||
{
 | 
			
		||||
							
								
								
									
										89
									
								
								server/src/Peripherals/MatrixKeyClient.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								server/src/Peripherals/MatrixKeyClient.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using DotNext;
 | 
			
		||||
 | 
			
		||||
namespace Peripherals.MatrixKeyClient;
 | 
			
		||||
 | 
			
		||||
class MatrixKeyAddr
 | 
			
		||||
{
 | 
			
		||||
    public const UInt32 BASE = 0x10_00_00_00;
 | 
			
		||||
    public const UInt32 KEY_ENABLE = BASE + 5;
 | 
			
		||||
    public const UInt32 KEY_CTRL = BASE + 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 矩阵键盘外设类,用于控制和管理矩阵键盘的功能。
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class MatrixKey
 | 
			
		||||
{
 | 
			
		||||
    readonly int timeout;
 | 
			
		||||
 | 
			
		||||
    readonly int port;
 | 
			
		||||
    readonly string address;
 | 
			
		||||
    private IPEndPoint ep;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数,用于初始化矩阵键盘外设实例。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">设备的IP地址</param>
 | 
			
		||||
    /// <param name="port">设备的端口号</param>
 | 
			
		||||
    /// <param name="timeout">操作的超时时间(毫秒),默认为1000</param>
 | 
			
		||||
    /// <returns>无返回值。</returns>
 | 
			
		||||
    public MatrixKey(string address, int port, int timeout = 1000)
 | 
			
		||||
    {
 | 
			
		||||
        this.address = address;
 | 
			
		||||
        this.port = port;
 | 
			
		||||
        this.ep = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        this.timeout = timeout;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启用矩阵键盘的控制功能。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>返回一个包含操作结果的异步任务</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> EnableControl()
 | 
			
		||||
    {
 | 
			
		||||
        if (MsgBus.IsRunning)
 | 
			
		||||
            await MsgBus.UDPServer.ClearUDPData(this.address);
 | 
			
		||||
        else return new(new Exception("Message Bus not work!"));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, MatrixKeyAddr.KEY_ENABLE, 1, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
        return ret.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 禁用矩阵键盘的控制功能。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>返回一个包含操作结果的异步任务</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> DisableControl()
 | 
			
		||||
    {
 | 
			
		||||
        if (MsgBus.IsRunning)
 | 
			
		||||
            await MsgBus.UDPServer.ClearUDPData(this.address);
 | 
			
		||||
        else return new(new Exception("Message Bus not work!"));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, MatrixKeyAddr.KEY_ENABLE, 0, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
        return ret.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 控制矩阵键盘的按键状态。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="keyStates">表示按键状态的位数组,长度必须为16</param>
 | 
			
		||||
    /// <returns>返回一个包含操作结果的异步任务</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> ControlKey(BitArray keyStates)
 | 
			
		||||
    {
 | 
			
		||||
        if (MsgBus.IsRunning)
 | 
			
		||||
            await MsgBus.UDPServer.ClearUDPData(this.address);
 | 
			
		||||
        else return new(new Exception("Message Bus not work!"));
 | 
			
		||||
 | 
			
		||||
        if (keyStates.Length != 16) return new(new ArgumentException(
 | 
			
		||||
            $"The number of key should be 16 instead of {keyStates.Length}", nameof(keyStates)));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(
 | 
			
		||||
            this.ep, MatrixKeyAddr.KEY_CTRL, Common.Number.BitsToNumber(keyStates).Value, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful) return new(ret.Error);
 | 
			
		||||
        return ret.Value;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1595
									
								
								src/APIClient.ts
									
									
									
									
									
								
							
							
						
						
									
										1595
									
								
								src/APIClient.ts
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user