Compare commits
6 Commits
ea7c2d425a
...
3883cd8304
Author | SHA1 | Date |
---|---|---|
|
3883cd8304 | |
|
2aa2f1dc37 | |
|
1bdcb672ab | |
|
7b1d1a5e87 | |
|
5bb011e685 | |
|
e3826f0ff6 |
|
@ -24,6 +24,9 @@ update:
|
||||||
gen-api:
|
gen-api:
|
||||||
npm run 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平台
|
# 构建服务器,包含win与linux平台
|
||||||
[working-directory: "server"]
|
[working-directory: "server"]
|
||||||
build-server self-contained=isSelfContained: _show-dir
|
build-server self-contained=isSelfContained: _show-dir
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"ts-results-es": "^5.0.1",
|
"ts-results-es": "^5.0.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
|
"yocto-queue": "^1.2.1",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -4075,6 +4076,18 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/yocto-queue": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yoctocolors": {
|
"node_modules/yoctocolors": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz",
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"ts-results-es": "^5.0.1",
|
"ts-results-es": "^5.0.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
|
"yocto-queue": "^1.2.1",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"author": "template",
|
||||||
|
"editor": "system",
|
||||||
|
"parts": [
|
||||||
|
{
|
||||||
|
"id": "board",
|
||||||
|
"type": "BaseBoard",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"attrs": {
|
||||||
|
"size": 1.2,
|
||||||
|
"width": 400,
|
||||||
|
"height": 400,
|
||||||
|
"roundCorner": 20
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": false,
|
||||||
|
"isOn": true,
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_0_0",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 50,
|
||||||
|
"y": 50,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "1",
|
||||||
|
"bindMatrixKey": "0",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_0_1",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 150,
|
||||||
|
"y": 50,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "2",
|
||||||
|
"bindMatrixKey": "1",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_0_2",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 250,
|
||||||
|
"y": 50,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "3",
|
||||||
|
"bindMatrixKey": "2",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_0_3",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 350,
|
||||||
|
"y": 50,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "A",
|
||||||
|
"bindMatrixKey": "3",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_1_0",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 50,
|
||||||
|
"y": 150,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "4",
|
||||||
|
"bindMatrixKey": "4",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_1_1",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 150,
|
||||||
|
"y": 150,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "5",
|
||||||
|
"bindMatrixKey": "5",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_1_2",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 250,
|
||||||
|
"y": 150,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "6",
|
||||||
|
"bindMatrixKey": "6",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_1_3",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 350,
|
||||||
|
"y": 150,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "B",
|
||||||
|
"bindMatrixKey": "7",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_2_0",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 50,
|
||||||
|
"y": 250,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "7",
|
||||||
|
"bindMatrixKey": "8",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_2_1",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 150,
|
||||||
|
"y": 250,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "8",
|
||||||
|
"bindMatrixKey": "9",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_2_2",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 250,
|
||||||
|
"y": 250,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "9",
|
||||||
|
"bindMatrixKey": "10",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_2_3",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 350,
|
||||||
|
"y": 250,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "C",
|
||||||
|
"bindMatrixKey": "11",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_3_0",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 50,
|
||||||
|
"y": 350,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "*",
|
||||||
|
"bindMatrixKey": "12",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_3_1",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 150,
|
||||||
|
"y": 350,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "0",
|
||||||
|
"bindMatrixKey": "13",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_3_2",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 250,
|
||||||
|
"y": 350,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "#",
|
||||||
|
"bindMatrixKey": "14",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "key_3_3",
|
||||||
|
"type": "MechanicalButton",
|
||||||
|
"x": 350,
|
||||||
|
"y": 350,
|
||||||
|
"attrs": {
|
||||||
|
"size": 0.5,
|
||||||
|
"bindKey": "D",
|
||||||
|
"bindMatrixKey": "15",
|
||||||
|
"pins": []
|
||||||
|
},
|
||||||
|
"rotate": 0,
|
||||||
|
"group": "MatrixKeypad",
|
||||||
|
"positionlock": false,
|
||||||
|
"hidepins": true,
|
||||||
|
"isOn": false,
|
||||||
|
"index": 15
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": []
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ try
|
||||||
{
|
{
|
||||||
options.AddPolicy("Users", policy => policy
|
options.AddPolicy("Users", policy => policy
|
||||||
.AllowAnyOrigin()
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyHeader()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class BoundaryScanRegs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("cell_name")]
|
[JsonProperty("cell_name")]
|
||||||
[JsonRequired]
|
[JsonRequired]
|
||||||
public string CellName { get; set; }
|
public string CellName { get; set; } = "UnknownCellName";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// [TODO:description]
|
/// [TODO:description]
|
||||||
|
@ -146,7 +146,8 @@ public class Parser
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>[TODO:return]</returns>
|
||||||
public Optional<List<BoundaryScanRegs.CellEntry>> GetBoundaryLogicalPorts()
|
public Optional<List<BoundaryScanRegs.CellEntry>> GetBoundaryLogicalPorts()
|
||||||
{
|
{
|
||||||
var registers = this.BoundaryRegsDesp["registers"]?.ToList().Where((item)=>{
|
var registers = this.BoundaryRegsDesp["registers"]?.ToList().Where((item) =>
|
||||||
|
{
|
||||||
return item["port_id"] is not null;
|
return item["port_id"] is not null;
|
||||||
});
|
});
|
||||||
if (registers is null) return new();
|
if (registers is null) return new();
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections;
|
||||||
using DotNext;
|
using DotNext;
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
|
@ -273,6 +274,21 @@ namespace Common
|
||||||
return ((srcBits >> location) & ((UInt32)0b1)) == 1;
|
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>
|
/// <summary>
|
||||||
/// 字符串转二进制字节数组
|
/// 字符串转二进制字节数组
|
||||||
|
|
|
@ -1,887 +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();
|
|
||||||
|
|
||||||
[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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
[HttpPost("EnabelMatrixKey")]
|
||||||
|
[EnableCors("Users")]
|
||||||
|
[ProducesResponseType(typeof(bool), 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>
|
||||||
|
[HttpPost("DisableMatrixKey")]
|
||||||
|
[EnableCors("Users")]
|
||||||
|
[ProducesResponseType(typeof(bool), 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>
|
||||||
|
[HttpPost("SetMatrixKeyStatus")]
|
||||||
|
[EnableCors("Users")]
|
||||||
|
[ProducesResponseType(typeof(bool), 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
/// <summary>
|
||||||
/// [TODO:description]
|
/// 边界扫描
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>返回所有引脚边界扫描结果</returns>
|
||||||
public async ValueTask<Result<BitArray>> BoundaryScan()
|
public async ValueTask<Result<BitArray>> BoundaryScan()
|
||||||
{
|
{
|
||||||
var paser = new BsdlParser.Parser();
|
var paser = new BsdlParser.Parser();
|
||||||
|
@ -824,9 +824,9 @@ public class Jtag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// [TODO:description]
|
/// 执行边界扫描并返回逻辑端口的状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>包含逻辑端口状态的字典,键为端口ID,值为布尔状态</returns>
|
||||||
public async ValueTask<Result<Dictionary<string, bool>>> BoundaryScanLogicalPorts()
|
public async ValueTask<Result<Dictionary<string, bool>>> BoundaryScanLogicalPorts()
|
||||||
{
|
{
|
||||||
var bitArray = await BoundaryScan();
|
var bitArray = await BoundaryScan();
|
||||||
|
@ -845,6 +845,11 @@ public class Jtag
|
||||||
return portStatus;
|
return portStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置JTAG的运行速度。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="speed">运行速度值。</param>
|
||||||
|
/// <returns>指示操作是否成功的异步结果。</returns>
|
||||||
public async ValueTask<Result<bool>> SetSpeed(UInt32 speed)
|
public async ValueTask<Result<bool>> SetSpeed(UInt32 speed)
|
||||||
{
|
{
|
||||||
// Clear Data
|
// Clear Data
|
||||||
|
@ -856,7 +861,7 @@ public class Jtag
|
||||||
JtagAddr.SPEED_CTRL, (speed << 16) | speed,
|
JtagAddr.SPEED_CTRL, (speed << 16) | speed,
|
||||||
JtagState.CMD_EXEC_FINISH, JtagState.CMD_EXEC_FINISH);
|
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;
|
return ret.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using DotNext;
|
using DotNext;
|
||||||
|
|
||||||
namespace DDSClient;
|
namespace Peripherals.DDSClient;
|
||||||
|
|
||||||
static class DDSAddr
|
static class DDSAddr
|
||||||
{
|
{
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ using WebProtocol;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UDPClientPool
|
public class UDPClientPool
|
||||||
{
|
{
|
||||||
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
private static IPAddress localhost = IPAddress.Parse("127.0.0.1");
|
private static IPAddress localhost = IPAddress.Parse("127.0.0.1");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -58,6 +60,9 @@ public class UDPClientPool
|
||||||
var sendLen = socket.SendTo(buf, endPoint);
|
var sendLen = socket.SendTo(buf, endPoint);
|
||||||
socket.Close();
|
socket.Close();
|
||||||
|
|
||||||
|
logger.Debug($"UDP socket send bytes to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
|
||||||
|
logger.Debug($" Original Data: {BitConverter.ToString(buf).Replace("-", " ")}");
|
||||||
|
|
||||||
if (sendLen == buf.Length) { return true; }
|
if (sendLen == buf.Length) { return true; }
|
||||||
else { return false; }
|
else { return false; }
|
||||||
}
|
}
|
||||||
|
@ -86,6 +91,10 @@ public class UDPClientPool
|
||||||
var sendLen = socket.SendTo(sendBytes, endPoint);
|
var sendLen = socket.SendTo(sendBytes, endPoint);
|
||||||
socket.Close();
|
socket.Close();
|
||||||
|
|
||||||
|
logger.Debug($"UDP socket send address package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
|
||||||
|
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
|
||||||
|
logger.Debug($" Decoded Data: {pkg.ToString()}");
|
||||||
|
|
||||||
if (sendLen == sendBytes.Length) { return true; }
|
if (sendLen == sendBytes.Length) { return true; }
|
||||||
else { return false; }
|
else { return false; }
|
||||||
}
|
}
|
||||||
|
@ -115,6 +124,9 @@ public class UDPClientPool
|
||||||
var sendLen = socket.SendTo(sendBytes, endPoint);
|
var sendLen = socket.SendTo(sendBytes, endPoint);
|
||||||
socket.Close();
|
socket.Close();
|
||||||
|
|
||||||
|
logger.Debug($"UDP socket send data package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
|
||||||
|
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
|
||||||
|
|
||||||
if (sendLen == sendBytes.Length) { return true; }
|
if (sendLen == sendBytes.Length) { return true; }
|
||||||
else { return false; }
|
else { return false; }
|
||||||
}
|
}
|
||||||
|
|
1608
src/APIClient.ts
1608
src/APIClient.ts
File diff suppressed because it is too large
Load Diff
|
@ -182,6 +182,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, shallowRef, onMounted } from "vue";
|
import { ref, computed, shallowRef, onMounted } from "vue";
|
||||||
import motherboardSvg from "../components/equipments/svg/motherboard.svg";
|
import motherboardSvg from "../components/equipments/svg/motherboard.svg";
|
||||||
|
import buttonSvg from "../components//equipments/svg/button.svg";
|
||||||
|
|
||||||
// Props 定义
|
// Props 定义
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -219,6 +220,7 @@ const availableComponents = [
|
||||||
{ type: "SMA", name: "SMA连接器" },
|
{ type: "SMA", name: "SMA连接器" },
|
||||||
{ type: "MotherBoard", name: "主板" },
|
{ type: "MotherBoard", name: "主板" },
|
||||||
{ type: "PG2L100H_FBG676", name: "PG2L100H FBG676芯片" },
|
{ type: "PG2L100H_FBG676", name: "PG2L100H FBG676芯片" },
|
||||||
|
{ type: "BaseBoard", name: "通用底板" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- 可用虚拟外设列表 ---
|
// --- 可用虚拟外设列表 ---
|
||||||
|
@ -233,6 +235,13 @@ const availableTemplates = ref([
|
||||||
path: "/EquipmentTemplates/PG2L100H_Pango100pro.json",
|
path: "/EquipmentTemplates/PG2L100H_Pango100pro.json",
|
||||||
thumbnailUrl: motherboardSvg,
|
thumbnailUrl: motherboardSvg,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "矩阵键盘",
|
||||||
|
id: "MatrixKey",
|
||||||
|
description: "包含4x4,共16个按键的矩阵键盘",
|
||||||
|
path: "/EquipmentTemplates/MatrixKey.json",
|
||||||
|
thumbnailUrl: buttonSvg,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 显示/隐藏组件菜单
|
// 显示/隐藏组件菜单
|
||||||
|
@ -372,6 +381,7 @@ async function addTemplate(template: any) {
|
||||||
id: template.id,
|
id: template.id,
|
||||||
name: template.name,
|
name: template.name,
|
||||||
template: templateData,
|
template: templateData,
|
||||||
|
capsPage: template.capsPage
|
||||||
});
|
});
|
||||||
|
|
||||||
// 关闭菜单
|
// 关闭菜单
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useDialogStore } from "@/stores/dialog";
|
import { useDialogStore } from "@/stores/dialog";
|
||||||
const dialog = useDialogStore();
|
const dialog = useDialogStore();
|
||||||
|
dialog.enableDialog = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="postcss">
|
<style scoped lang="postcss">
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" :class="$attrs">
|
||||||
|
<rect :width="width" :height="height" :rx="props.roundCorner" fill="#222222" />
|
||||||
|
</svg>
|
||||||
|
<Teleport to="#ComponentCapabilities" v-if="selectecComponentID === props.componentId && !!slot.default">
|
||||||
|
<slot></slot>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, inject } from "vue";
|
||||||
|
import { CanvasCurrentSelectedComponentID } from "../InjectKeys";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
size?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
roundCorner?: number;
|
||||||
|
componentId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = defineSlots();
|
||||||
|
const props = withDefaults(defineProps<Props>(), getDefaultProps());
|
||||||
|
const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null));
|
||||||
|
|
||||||
|
// 计算实际宽高
|
||||||
|
const width = computed(() => props.width * props.size);
|
||||||
|
const height = computed(() => props.height * props.size);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export function getDefaultProps(): Props {
|
||||||
|
return {
|
||||||
|
size: 1,
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
roundCorner: 20,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="postcss"></style>
|
|
@ -80,16 +80,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, computed } from "vue";
|
import { ref, onMounted, onUnmounted, computed } from "vue";
|
||||||
import Pin from "./Pin.vue";
|
import Pin from "./Pin.vue";
|
||||||
|
import { useEquipments } from "@/stores/equipments";
|
||||||
|
import { useDialogStore } from "@/stores/dialog";
|
||||||
import { useConstraintsStore } from "../../stores/constraints";
|
import { useConstraintsStore } from "../../stores/constraints";
|
||||||
const { notifyConstraintChange } = useConstraintsStore();
|
import { isNull, isUndefined } from "mathjs";
|
||||||
|
import z from "zod";
|
||||||
// 存储多个Pin引用
|
import { toNumber } from "lodash";
|
||||||
const pinRefs = ref<Record<string, any>>({});
|
|
||||||
|
|
||||||
// 按钮特有属性
|
// 按钮特有属性
|
||||||
interface ButtonProps {
|
export interface ButtonProps {
|
||||||
size?: number;
|
size: number;
|
||||||
bindKey?: string;
|
|
||||||
componentId?: string;
|
componentId?: string;
|
||||||
pins?: {
|
pins?: {
|
||||||
pinId: string;
|
pinId: string;
|
||||||
|
@ -97,21 +97,20 @@ interface ButtonProps {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
bindKey?: string;
|
||||||
|
bindMatrixKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
const props = defineProps<ButtonProps>();
|
||||||
size: 1,
|
|
||||||
bindKey: "",
|
// Global Stores
|
||||||
componentId: "button-default",
|
const constrainsts = useConstraintsStore();
|
||||||
pins: () => [
|
const dialog = useDialogStore();
|
||||||
{
|
const eqps = useEquipments();
|
||||||
pinId: "BTN",
|
|
||||||
constraint: "",
|
// 存储多个Pin引用
|
||||||
x: 80,
|
const pinRefs = ref<Record<string, any>>({});
|
||||||
y: 140,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// 计算实际宽高
|
// 计算实际宽高
|
||||||
const width = computed(() => 160 * props.size);
|
const width = computed(() => 160 * props.size);
|
||||||
|
@ -153,15 +152,27 @@ function toggleButtonState(isPressed: boolean) {
|
||||||
isKeyPressed.value = isPressed;
|
isKeyPressed.value = isPressed;
|
||||||
btnHeight.value = isPressed ? 180 : 200;
|
btnHeight.value = isPressed ? 180 : 200;
|
||||||
|
|
||||||
|
// 矩阵键盘
|
||||||
|
if (eqps.enableMatrixKey) {
|
||||||
|
const ret = eqps.setMatrixKey(props.bindMatrixKey, isPressed);
|
||||||
|
|
||||||
|
if (ret) eqps.matrixKeypadSetKeyStates(eqps.matrixKeyStates);
|
||||||
|
else
|
||||||
|
dialog.error(
|
||||||
|
`绑定的矩阵键盘值只能是0 ~ 15,而不是: ${props.bindMatrixKey}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 发出事件通知父组件
|
// 发出事件通知父组件
|
||||||
if (isPressed) {
|
if (isPressed) {
|
||||||
emit("press");
|
emit("press");
|
||||||
// 如果有约束,通知约束状态变化为高电平
|
|
||||||
// 对所有引脚应用相同的状态
|
|
||||||
if (props.pins) {
|
if (props.pins) {
|
||||||
|
// 如果有约束,通知约束状态变化为高电平
|
||||||
|
// 对所有引脚应用相同的状态
|
||||||
props.pins.forEach((pin) => {
|
props.pins.forEach((pin) => {
|
||||||
if (pin.constraint) {
|
if (pin.constraint) {
|
||||||
notifyConstraintChange(pin.constraint, "high");
|
constrainsts.notifyConstraintChange(pin.constraint, "high");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -172,7 +183,7 @@ function toggleButtonState(isPressed: boolean) {
|
||||||
if (props.pins) {
|
if (props.pins) {
|
||||||
props.pins.forEach((pin) => {
|
props.pins.forEach((pin) => {
|
||||||
if (pin.constraint) {
|
if (pin.constraint) {
|
||||||
notifyConstraintChange(pin.constraint, "low");
|
constrainsts.notifyConstraintChange(pin.constraint, "low");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -200,35 +211,37 @@ onUnmounted(() => {
|
||||||
defineExpose({
|
defineExpose({
|
||||||
toggleButtonState,
|
toggleButtonState,
|
||||||
getInfo: () => ({
|
getInfo: () => ({
|
||||||
// 按钮特有属性
|
|
||||||
bindKey: props.bindKey,
|
bindKey: props.bindKey,
|
||||||
componentId: props.componentId,
|
componentId: props.componentId,
|
||||||
pins: props.pins,
|
pins: props.pins,
|
||||||
}),
|
}),
|
||||||
// 获取引脚位置
|
// 获取引脚位置
|
||||||
getPinPosition: (pinId: string) => {
|
getPinPosition: (pinId: string) => {
|
||||||
console.log(`[MechanicalButton] 调用getPinPosition,寻找pinId: ${pinId}`);
|
console.debug(`[MechanicalButton] 调用getPinPosition,寻找pinId: ${pinId}`);
|
||||||
console.log(
|
console.debug(
|
||||||
`[MechanicalButton] 组件ID: ${props.componentId}, 当前尺寸: ${props.size}, 组件宽高: ${width.value}x${height.value}`,
|
`[MechanicalButton] 组件ID: ${props.componentId}, 当前尺寸: ${props.size}, 组件宽高: ${width.value}x${height.value}`,
|
||||||
);
|
);
|
||||||
console.log(`[MechanicalButton] 当前存在的pins:`, props.pins);
|
console.debug(`[MechanicalButton] 当前存在的pins:`, props.pins);
|
||||||
|
|
||||||
// 如果是自定义的引脚ID
|
// 如果是自定义的引脚ID
|
||||||
if (props.pins && props.pins.length > 0) {
|
if (props.pins && props.pins.length > 0) {
|
||||||
const customPin = props.pins.find((p) => p.pinId === pinId);
|
const customPin = props.pins.find((p) => p.pinId === pinId);
|
||||||
|
|
||||||
if (customPin) {
|
if (customPin) {
|
||||||
console.log(`[MechanicalButton] 找到自定义引脚: ${pinId},配置位置:`, {
|
console.debug(
|
||||||
x: customPin.x,
|
`[MechanicalButton] 找到自定义引脚: ${pinId},配置位置:`,
|
||||||
y: customPin.y,
|
{
|
||||||
});
|
x: customPin.x,
|
||||||
|
y: customPin.y,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// 考虑组件尺寸的缩放
|
// 考虑组件尺寸的缩放
|
||||||
// 这里的x和y是针对标准尺寸(size=1)的坐标,需要根据实际size调整
|
// 这里的x和y是针对标准尺寸(size=1)的坐标,需要根据实际size调整
|
||||||
const scaledX = customPin.x * props.size;
|
const scaledX = customPin.x * props.size;
|
||||||
const scaledY = customPin.y * props.size;
|
const scaledY = customPin.y * props.size;
|
||||||
|
|
||||||
console.log(`[MechanicalButton] 返回缩放后的坐标:`, {
|
console.debug(`[MechanicalButton] 返回缩放后的坐标:`, {
|
||||||
x: scaledX,
|
x: scaledX,
|
||||||
y: scaledY,
|
y: scaledY,
|
||||||
});
|
});
|
||||||
|
@ -237,12 +250,12 @@ defineExpose({
|
||||||
y: scaledY,
|
y: scaledY,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.log(`[MechanicalButton] 未找到pinId: ${pinId}的引脚配置`);
|
console.debug(`[MechanicalButton] 未找到pinId: ${pinId}的引脚配置`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`[MechanicalButton] 没有配置任何引脚`);
|
console.debug(`[MechanicalButton] 没有配置任何引脚`);
|
||||||
}
|
}
|
||||||
console.log(`[MechanicalButton] 返回null,未找到引脚`);
|
console.debug(`[MechanicalButton] 返回null,未找到引脚`);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -253,7 +266,6 @@ defineExpose({
|
||||||
export function getDefaultProps() {
|
export function getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
size: 1,
|
size: 1,
|
||||||
bindKey: "",
|
|
||||||
pins: [
|
pins: [
|
||||||
{
|
{
|
||||||
pinId: "BTN",
|
pinId: "BTN",
|
||||||
|
@ -262,6 +274,8 @@ export function getDefaultProps() {
|
||||||
y: 140,
|
y: 140,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
bindKey: "",
|
||||||
|
bindMatrixKey: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { toNumber } from "lodash";
|
||||||
|
|
||||||
// 主板特有属性
|
// 主板特有属性
|
||||||
export interface MotherBoardProps {
|
export interface MotherBoardProps {
|
||||||
size?: number;
|
size: number;
|
||||||
boardAddr?: string;
|
boardAddr?: string;
|
||||||
boardPort?: string;
|
boardPort?: string;
|
||||||
componentId?: string;
|
componentId?: string;
|
||||||
|
@ -63,7 +63,6 @@ export function getDefaultProps(): MotherBoardProps {
|
||||||
size: 1,
|
size: 1,
|
||||||
boardAddr: "127.0.0.1",
|
boardAddr: "127.0.0.1",
|
||||||
boardPort: "1234",
|
boardPort: "1234",
|
||||||
componentId: "DefaultMotherBoardID",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -44,6 +44,12 @@
|
||||||
{{ eqps.enableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }}
|
{{ eqps.enableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<h1 class="font-bold text-center text-2xl">外设</h1>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<input type="checkbox" class="checkbox" :checked="eqps.enableMatrixKey" @change="handleMatrixkeyCheckboxChange" />
|
||||||
|
<p class="mx-2">启用矩阵键盘</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -52,7 +58,7 @@ import z from "zod";
|
||||||
import UploadCard from "@/components/UploadCard.vue";
|
import UploadCard from "@/components/UploadCard.vue";
|
||||||
import { useDialogStore } from "@/stores/dialog";
|
import { useDialogStore } from "@/stores/dialog";
|
||||||
import { useEquipments } from "@/stores/equipments";
|
import { useEquipments } from "@/stores/equipments";
|
||||||
import { computed, ref, watchEffect } from "vue";
|
import { computed, ref, watchEffect, watchPostEffect } from "vue";
|
||||||
|
|
||||||
interface CapsProps {
|
interface CapsProps {
|
||||||
jtagAddr?: string;
|
jtagAddr?: string;
|
||||||
|
@ -100,6 +106,19 @@ function handleSelectJtagSpeed(event: Event) {
|
||||||
emits("changeJtagFreq", target.value);
|
emits("changeJtagFreq", target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleMatrixkeyCheckboxChange(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
if (target.checked) {
|
||||||
|
const ret = await eqps.matrixKeypadEnable(true);
|
||||||
|
if (!ret) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const ret = await eqps.matrixKeypadEnable(false);
|
||||||
|
if (!ret) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function toggleJtagBoundaryScan() {
|
async function toggleJtagBoundaryScan() {
|
||||||
if (eqps.jtagClientMutex.isLocked()) {
|
if (eqps.jtagClientMutex.isLocked()) {
|
||||||
dialog.warn("Jtag正在被占用");
|
dialog.warn("Jtag正在被占用");
|
||||||
|
|
|
@ -1,40 +1,61 @@
|
||||||
import { ref, computed } from 'vue'
|
import { ref, reactive, watchPostEffect, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
|
import Queue from 'yocto-queue';
|
||||||
|
|
||||||
export const useDialogStore = defineStore('dialog', () => {
|
export const useDialogStore = defineStore('dialog', () => {
|
||||||
type Title = "Error" | "Info" | "Warn";
|
type Title = "Error" | "Info" | "Warn";
|
||||||
|
const enableDialog = ref(false);
|
||||||
const isDialogOpen = ref(false);
|
const isDialogOpen = ref(false);
|
||||||
const dialogTitle = ref<Title>("Error");
|
const dialogTitle = ref<Title>("Error");
|
||||||
const dialogContent = ref("这是一个错误");
|
const dialogContent = ref("这是一个错误");
|
||||||
|
const contentQueue = new Queue<{ type: Title; content: string }>()
|
||||||
|
|
||||||
function openDialog(title: Title, content?: string) {
|
function openDialog(title?: Title, content?: string) {
|
||||||
if (!isUndefined(content) && content.length != 0)
|
if (isUndefined(title)) {
|
||||||
dialogContent.value = content;
|
if (contentQueue.size != 0) {
|
||||||
dialogTitle.value = title;
|
const dialog = contentQueue.dequeue();
|
||||||
isDialogOpen.value = true;
|
if (isUndefined(dialog)) return;
|
||||||
|
openDialog(dialog.type, dialog.content);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isUndefined(content) && content.length != 0)
|
||||||
|
dialogContent.value = content;
|
||||||
|
dialogTitle.value = title;
|
||||||
|
isDialogOpen.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDialog() {
|
function closeDialog() {
|
||||||
isDialogOpen.value = false;
|
isDialogOpen.value = false;
|
||||||
|
|
||||||
|
openDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
function info(content?: string) {
|
function info(content: string) {
|
||||||
openDialog("Info", content);
|
contentQueue.enqueue({ type: "Info", content: content });
|
||||||
|
openDialog();
|
||||||
|
// openDialog("Info", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(content?: string) {
|
function error(content: string) {
|
||||||
openDialog("Error", content);
|
contentQueue.enqueue({ type: "Error", content: content });
|
||||||
|
openDialog();
|
||||||
|
// openDialog("Error", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function warn(content?: string) {
|
function warn(content: string) {
|
||||||
openDialog("Warn", content);
|
contentQueue.enqueue({ type: "Warn", content: content });
|
||||||
|
openDialog();
|
||||||
|
// openDialog("Warn", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
enableDialog,
|
||||||
isDialogOpen,
|
isDialogOpen,
|
||||||
dialogTitle,
|
dialogTitle,
|
||||||
dialogContent,
|
dialogContent,
|
||||||
|
dialogQueue: contentQueue,
|
||||||
openDialog,
|
openDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
info,
|
info,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { ref, watchEffect } from 'vue'
|
import { ref, reactive, watchPostEffect } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { isString, toNumber, isUndefined } from 'lodash';
|
import { isString, toNumber } from 'lodash';
|
||||||
import { Common } from '@/Common';
|
import { Common } from '@/Common';
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { isNumber } from 'mathjs';
|
import { isNumber } from 'mathjs';
|
||||||
import { JtagClient } from "@/APIClient";
|
import { JtagClient, MatrixKeyClient } from "@/APIClient";
|
||||||
import { Mutex, withTimeout } from 'async-mutex';
|
import { Mutex, withTimeout } from 'async-mutex';
|
||||||
import { useConstraintsStore } from "@/stores/constraints";
|
import { useConstraintsStore } from "@/stores/constraints";
|
||||||
import { useDialogStore } from './dialog';
|
import { useDialogStore } from './dialog';
|
||||||
|
@ -14,15 +14,31 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
const constrainsts = useConstraintsStore();
|
const constrainsts = useConstraintsStore();
|
||||||
const dialog = useDialogStore();
|
const dialog = useDialogStore();
|
||||||
|
|
||||||
|
// Basic Info
|
||||||
const boardAddr = ref("127.0.0.1");
|
const boardAddr = ref("127.0.0.1");
|
||||||
const boardPort = ref(1234);
|
const boardPort = ref(1234);
|
||||||
|
|
||||||
|
// Jtag
|
||||||
const jtagBitstream = ref<File>();
|
const jtagBitstream = ref<File>();
|
||||||
const jtagBoundaryScanFreq = ref(100);
|
const jtagBoundaryScanFreq = ref(100);
|
||||||
const jtagClientMutex = withTimeout(new Mutex(), 1000, new Error("JtagClient Mutex Timeout!"))
|
const jtagClientMutex = withTimeout(new Mutex(), 1000, new Error("JtagClient Mutex Timeout!"))
|
||||||
const jtagClient = new JtagClient();
|
const jtagClient = new JtagClient();
|
||||||
|
|
||||||
const enableJtagBoundaryScan = ref(false);
|
// Matrix Key
|
||||||
|
const matrixKeyStates = reactive(new Array<boolean>(16).fill(false))
|
||||||
|
const matrixKeypadClientMutex = withTimeout(new Mutex(), 1000, new Error("Matrixkeyclient Mutex Timeout!"));
|
||||||
|
const matrixKeypadClient = new MatrixKeyClient();
|
||||||
|
|
||||||
|
// Enable Setting
|
||||||
|
const enableJtagBoundaryScan = ref(false);
|
||||||
|
const enableMatrixKey = ref(false);
|
||||||
|
|
||||||
|
// Watch
|
||||||
|
watchPostEffect(async () => {
|
||||||
|
if (true === enableJtagBoundaryScan.value) jtagBoundaryScan();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse and Set
|
||||||
function setAddr(address: string | undefined): boolean {
|
function setAddr(address: string | undefined): boolean {
|
||||||
if (isString(address) && z.string().ip("4").safeParse(address).success) {
|
if (isString(address) && z.string().ip("4").safeParse(address).success) {
|
||||||
boardAddr.value = address;
|
boardAddr.value = address;
|
||||||
|
@ -49,9 +65,22 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
function setMatrixKey(keyNum: number | string | undefined, keyValue: boolean): boolean {
|
||||||
if (enableJtagBoundaryScan.value) jtagBoundaryScan();
|
let _keyNum: number;
|
||||||
});
|
if (isString(keyNum)) {
|
||||||
|
_keyNum = toNumber(keyNum);
|
||||||
|
} else if (isNumber(keyNum)) {
|
||||||
|
_keyNum = keyNum;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z.number().nonnegative().max(16).safeParse(_keyNum).success) {
|
||||||
|
matrixKeyStates[_keyNum] = keyValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
async function jtagBoundaryScan() {
|
async function jtagBoundaryScan() {
|
||||||
const release = await jtagClientMutex.acquire();
|
const release = await jtagClientMutex.acquire();
|
||||||
|
@ -138,11 +167,60 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function matrixKeypadSetKeyStates(keyStates: boolean[]) {
|
||||||
|
const release = await matrixKeypadClientMutex.acquire();
|
||||||
|
console.log("set Key !!!!!!!!!!!!");
|
||||||
|
try {
|
||||||
|
const resp = await matrixKeypadClient.setMatrixKeyStatus(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value,
|
||||||
|
keyStates
|
||||||
|
);
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
dialog.error("设置矩阵键盘时,服务器发生错误");
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function matrixKeypadEnable(enable: boolean) {
|
||||||
|
const release = await matrixKeypadClientMutex.acquire();
|
||||||
|
try {
|
||||||
|
if (enable) {
|
||||||
|
const resp = await matrixKeypadClient.enabelMatrixKey(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value,
|
||||||
|
);
|
||||||
|
enableMatrixKey.value = resp;
|
||||||
|
return resp;
|
||||||
|
} else {
|
||||||
|
const resp = await matrixKeypadClient.disableMatrixKey(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value,
|
||||||
|
);
|
||||||
|
enableMatrixKey.value = !resp;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
enableMatrixKey.value = false;
|
||||||
|
dialog.error("设置矩阵键盘是否启用时,服务器发生错误");
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boardAddr,
|
boardAddr,
|
||||||
boardPort,
|
boardPort,
|
||||||
setAddr,
|
setAddr,
|
||||||
setPort,
|
setPort,
|
||||||
|
setMatrixKey,
|
||||||
|
|
||||||
|
// Jtag
|
||||||
|
enableJtagBoundaryScan,
|
||||||
jtagBitstream,
|
jtagBitstream,
|
||||||
jtagBoundaryScanFreq,
|
jtagBoundaryScanFreq,
|
||||||
jtagClientMutex,
|
jtagClientMutex,
|
||||||
|
@ -151,7 +229,14 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
jtagDownloadBitstream,
|
jtagDownloadBitstream,
|
||||||
jtagGetIDCode,
|
jtagGetIDCode,
|
||||||
jtagSetSpeed,
|
jtagSetSpeed,
|
||||||
enableJtagBoundaryScan,
|
|
||||||
|
// Matrix Key
|
||||||
|
enableMatrixKey,
|
||||||
|
matrixKeyStates,
|
||||||
|
matrixKeypadClientMutex,
|
||||||
|
matrixKeypadClient,
|
||||||
|
matrixKeypadEnable,
|
||||||
|
matrixKeypadSetKeyStates,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue