Merge branch 'csharp'

This commit is contained in:
SikongJueluo 2025-05-13 13:00:37 +08:00
commit 099d44663d
No known key found for this signature in database
24 changed files with 1163 additions and 197 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ lerna-debug.log*
node_modules node_modules
.DS_Store .DS_Store
dist dist
**/wwwroot
dist-ssr dist-ssr
coverage coverage
*.local *.local

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "server/src/BsdlParser"]
path = server/src/BsdlParser
url = git@github.com:SikongJueluo/python-bsdl-parser.git

View File

@ -1,3 +1,5 @@
isSelfContained := "false"
@_show-dir: @_show-dir:
echo "Current Working Directory:" echo "Current Working Directory:"
pwd pwd
@ -11,6 +13,11 @@ clean:
rm -rf "server.test/obj" rm -rf "server.test/obj"
rm -rf "dist" rm -rf "dist"
update:
npm install
cd server && dotnet restore
git submodule update --init --remote --recursive
# 生成Restful API到网页客户端 # 生成Restful API到网页客户端
gen-api: gen-api:
cd server && dotnet run & cd server && dotnet run &
@ -19,16 +26,25 @@ gen-api:
# 构建服务器包含win与linux平台 # 构建服务器包含win与linux平台
[working-directory: "server"] [working-directory: "server"]
build-server: _show-dir build-server self-contained=isSelfContained: _show-dir
dotnet publish --self-contained false -t:PublishAllRids dotnet publish --self-contained {{self-contained}} -t:PublishAllRids
npm run build
rsync -avz --delete ../wwwroot/ ./bin/Release/net9.0/linux-x64/publish/wwwroot/
rsync -avz --delete ../wwwroot/ ./bin/Release/net9.0/win-x64/publish/wwwroot/
# 运行服务器 run-server: (build-server "true")
[working-directory: "server"] exec ./server/bin/Release/net9.0/linux-x64/publish/server
run-server: _show-dir
dotnet run run-web:
npm run build
npm run preview
# 测试服务器
dev-server: _show-dir
cd server && dotnet run
# 运行网页客户端 # 运行网页客户端
run-web: dev-web:
npm run dev npm run dev
# 运行测试用例测试服务器 # 运行测试用例测试服务器

24
package-lock.json generated
View File

@ -25,6 +25,7 @@
"@tailwindcss/postcss": "^4.0.12", "@tailwindcss/postcss": "^4.0.12",
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",
"@types/node": "^22.13.4", "@types/node": "^22.13.4",
"@vitejs/plugin-basic-ssl": "^2.0.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1", "@vitejs/plugin-vue-jsx": "^4.1.1",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",
@ -1646,6 +1647,19 @@
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/@vitejs/plugin-basic-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.0.0.tgz",
"integrity": "sha512-gc9Tjg8bUxBVSTzeWT3Njc0Cl3PakHFKdNfABnZWiUgbxqmHDEn7uECv3fHVylxoYgNzAcmU7ZrILz+BwSo3sA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"peerDependencies": {
"vite": "^6.0.0"
}
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "5.2.3", "version": "5.2.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz",
@ -3785,18 +3799,18 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.3.2", "version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.4.3", "fdir": "^6.4.4",
"picomatch": "^4.0.2", "picomatch": "^4.0.2",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"rollup": "^4.34.9", "rollup": "^4.34.9",
"tinyglobby": "^0.2.12" "tinyglobby": "^0.2.13"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"

View File

@ -4,7 +4,7 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --host",
"build": "run-p type-check \"build-only {@}\" --", "build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
@ -31,6 +31,7 @@
"@tailwindcss/postcss": "^4.0.12", "@tailwindcss/postcss": "^4.0.12",
"@tsconfig/node22": "^22.0.0", "@tsconfig/node22": "^22.0.0",
"@types/node": "^22.13.4", "@types/node": "^22.13.4",
"@vitejs/plugin-basic-ssl": "^2.0.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1", "@vitejs/plugin-vue-jsx": "^4.1.1",
"@vue/tsconfig": "^0.7.0", "@vue/tsconfig": "^0.7.0",

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.FileProviders;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog; using NLog;
using NLog.Web; using NLog.Web;
@ -9,7 +10,6 @@ var logger = NLog.LogManager.Setup()
.GetCurrentClassLogger(); .GetCurrentClassLogger();
logger.Debug("Init Main..."); logger.Debug("Init Main...");
try try
{ {
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -48,6 +48,12 @@ try
); );
}); });
} }
builder.Services.AddCors(options =>
{
options.AddPolicy("Users", policy => policy
.AllowAnyOrigin()
);
});
// Add Swagger // Add Swagger
builder.Services.AddControllers(); builder.Services.AddControllers();
@ -78,34 +84,39 @@ try
// Application Settings // Application Settings
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
// app.UseExceptionHandler(new ExceptionHandlerOptions() if (!app.Environment.IsDevelopment())
// { {
// AllowStatusCode404Response = true, // app.UseExceptionHandler("/Home/Error");
// ExceptionHandlingPath = "/error" // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
// }); app.UseHsts();
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); // Serve static files
logger.Info($"Use Static Files : {Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")}");
app.UseDefaultFiles();
app.UseStaticFiles(); // Serves files from wwwroot by default
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "assets")),
RequestPath = "/assets"
});
app.MapFallbackToFile("index.html");
}
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseCors(); app.UseCors();
app.UseAuthorization(); app.UseAuthorization();
// if (app.Environment.IsDevelopment()) // Swagger
// {
app.UseOpenApi(); app.UseOpenApi();
app.UseSwaggerUi(); app.UseSwaggerUi();
// }
// Router
app.MapControllers();
// Setup Program // Setup Program
MsgBus.Init(); MsgBus.Init();
// Router app.Run();
// API Get
app.MapGet("/", () => Results.Redirect("/swagger"));
app.MapControllers();
app.Run("http://localhost:5000");
} }
catch (Exception exception) catch (Exception exception)
{ {

View File

@ -5,18 +5,20 @@
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "http://localhost:5188", "applicationUrl": "http://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
} }
}, },
"https": { "https": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": true,
"applicationUrl": "https://localhost:7070;http://localhost:5188", "applicationUrl": "https://localhost:7278;http://localhost:5000",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
} }
} }
} }

View File

@ -1,27 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="PublishAllRids.targets" /> <Import Project="PublishAllRids.xml" />
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<RuntimeIdentifiers>win-x64;linux-x64;</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64;</RuntimeIdentifiers>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
</PropertyGroup> <SpaRoot>../</SpaRoot>
<SpaProxyServerUrl>http://localhost:5173</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DotNext" Version="5.19.1" /> <PackageReference Include="DotNext" Version="5.19.1" />
<PackageReference Include="DotNext.Threading" Version="5.19.1" /> <PackageReference Include="DotNext.Threading" Version="5.19.1" />
<PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" /> <PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
<PackageReference Include="linq2db.AspNet" Version="5.4.1" /> <PackageReference Include="linq2db.AspNet" Version="5.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.4" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.23" /> <PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.4.0" /> <PackageReference Include="NLog" Version="5.4.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" />
<PackageReference Include="NSwag.AspNetCore" Version="14.3.0" /> <PackageReference Include="NSwag.AspNetCore" Version="14.3.0" />
<PackageReference Include="pythonnet" Version="3.0.5" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" /> <PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
server/src/BsdlParser Submodule

@ -0,0 +1 @@
Subproject commit ac164eb16d7d9a9a387ff1f62cef249c65565bef

View File

@ -41,6 +41,7 @@ namespace Common
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
}; };
/// <summary> /// <summary>
/// 整数转成二进制字节数组 /// 整数转成二进制字节数组
/// </summary> /// </summary>
@ -166,6 +167,26 @@ namespace Common
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="uintArray">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static Result<byte[]> UInt32ArrayToBytes(UInt32[] uintArray)
{
byte[] byteArray = new byte[uintArray.Length * 4];
try
{
Buffer.BlockCopy(uintArray, 0, byteArray, 0, uintArray.Length * 4);
return byteArray;
}
catch (Exception error)
{
return new(error);
}
}
/// <summary> /// <summary>
/// 比特合并成二进制字节 /// 比特合并成二进制字节

View File

@ -8,6 +8,44 @@ using WebProtocol;
namespace server.Controllers; namespace server.Controllers;
// /// <summary>
// /// [TODO:description]
// /// </summary>
// public class HomeController : ControllerBase
// {
// private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
//
// string INDEX_HTML_PATH = Path.Combine(Environment.CurrentDirectory, "index.html");
//
// /// <summary>
// /// [TODO:description]
// /// </summary>
// /// <returns>[TODO:return]</returns>
// [HttpGet("/")]
// public IResult Index()
// {
// return TypedResults.Content("Hello", "text/html");
// }
//
// // [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
// // public IResult Error()
// // {
// // return TypedResults.Ok();
// // }
//
// /// <summary>
// /// [TODO:description]
// /// </summary>
// /// <returns>[TODO:return]</returns>
// [HttpGet("/hello")]
// [HttpPost("/hello")]
// public IActionResult Hello()
// {
// string randomString = Guid.NewGuid().ToString();
// return this.Ok($"Hello World! GUID: {randomString}");
// }
// }
/// <summary> /// <summary>
/// UDP API /// UDP API
/// </summary> /// </summary>
@ -180,8 +218,8 @@ public class JtagController : ControllerBase
/// <param name="address"> 设备地址 </param> /// <param name="address"> 设备地址 </param>
/// <param name="port"> 设备端口 </param> /// <param name="port"> 设备端口 </param>
[HttpGet("GetDeviceIDCode")] [HttpGet("GetDeviceIDCode")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> GetDeviceIDCode(string address, int port) public async ValueTask<IResult> GetDeviceIDCode(string address, int port)
{ {
var jtagCtrl = new JtagClient.Jtag(address, port); var jtagCtrl = new JtagClient.Jtag(address, port);
@ -237,8 +275,9 @@ public class JtagController : ControllerBase
/// <param name="address"> 设备地址 </param> /// <param name="address"> 设备地址 </param>
/// <param name="file">比特流文件</param> /// <param name="file">比特流文件</param>
[HttpPost("UploadBitstream")] [HttpPost("UploadBitstream")]
[ProducesResponseType(StatusCodes.Status200OK)] [EnableCors("Users")]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
public async ValueTask<IResult> UploadBitstream(string address, IFormFile file) public async ValueTask<IResult> UploadBitstream(string address, IFormFile file)
{ {
if (file == null || file.Length == 0) if (file == null || file.Length == 0)
@ -263,7 +302,7 @@ public class JtagController : ControllerBase
} }
logger.Info($"Device {address} Upload Bitstream Successfully"); logger.Info($"Device {address} Upload Bitstream Successfully");
return TypedResults.Ok("Bitstream Upload Successfully"); return TypedResults.Ok(true);
} }
/// <summary> /// <summary>
@ -272,9 +311,10 @@ public class JtagController : ControllerBase
/// <param name="address"> 设备地址 </param> /// <param name="address"> 设备地址 </param>
/// <param name="port"> 设备端口 </param> /// <param name="port"> 设备端口 </param>
[HttpPost("DownloadBitstream")] [HttpPost("DownloadBitstream")]
[ProducesResponseType(StatusCodes.Status200OK)] [EnableCors("Users")]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> DownloadBitstream(string address, int port) public async ValueTask<IResult> DownloadBitstream(string address, int port)
{ {
// 检查文件 // 检查文件
@ -344,6 +384,32 @@ public class JtagController : ControllerBase
} }
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="address">[TODO:parameter]</param>
/// <param name="port">[TODO:parameter]</param>
/// <param name="portNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
[HttpPost("BoundaryScan")]
[EnableCors("Users")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> BoundaryScan(string address, int port, int portNum)
{
var jtagCtrl = new JtagClient.Jtag(address, port);
var ret = await jtagCtrl.BoundaryScan(portNum);
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>

View File

@ -1,3 +1,4 @@
using System.Collections;
using System.Net; using System.Net;
using DotNext; using DotNext;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -730,13 +731,13 @@ public class Jtag
return true; return true;
} }
async ValueTask<Result<UInt32>> LoadDRCareOutput(UInt32 bytesLen) async ValueTask<Result<UInt32>> LoadDRCareOutput(UInt32 UInt32Num)
{ {
if (bytesLen > Math.Pow(2, 28)) return new(new Exception("Length is over 2^(28 - 3)")); if (UInt32Num > Math.Pow(2, 23)) return new(new Exception("Length is over 2^(28 - 5)"));
var ret = await WriteFIFO( var ret = await WriteFIFO(
JtagAddr.WRITE_CMD, JtagAddr.WRITE_CMD,
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 8 * bytesLen, 28).Value, Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 32 * UInt32Num, 28).Value,
0x01_00_00_00, JtagState.CMD_EXEC_FINISH); 0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
if (ret.Value) if (ret.Value)
@ -745,6 +746,31 @@ public class Jtag
return new(new Exception("LoadDRCareo Failed!")); return new(new Exception("LoadDRCareo Failed!"));
} }
async ValueTask<Result<UInt32[]>> LoadDRCareOutputArray(UInt32 UInt32Num)
{
if (UInt32Num > Math.Pow(2, 23)) return new(new Exception("Length is over 2^(28 - 5)"));
var ret = await WriteFIFO(
JtagAddr.WRITE_CMD,
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 32 * UInt32Num, 28).Value,
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
if (ret.Value)
{
var array = new UInt32[UInt32Num];
for (int i = 0; i < UInt32Num; i++)
{
var retData = await ReadFIFO(JtagAddr.READ_DATA);
if (!retData.IsSuccessful)
return new(new Exception("Read FIFO failed when Load DR"));
array[i] = retData.Value;
}
return array;
}
else
return new(new Exception("LoadDRCareo Failed!"));
}
/// <summary> /// <summary>
/// 读取 JTAG 设备的 ID 代码 /// 读取 JTAG 设备的 ID 代码
/// </summary> /// </summary>
@ -774,7 +800,7 @@ public class Jtag
if (!ret.IsSuccessful) return new(ret.Error); if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed")); else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed"));
var retData = await LoadDRCareOutput(4); var retData = await LoadDRCareOutput(1);
if (!retData.IsSuccessful) if (!retData.IsSuccessful)
{ {
return new(new Exception("Get ID Code Failed")); return new(new Exception("Get ID Code Failed"));
@ -812,11 +838,9 @@ public class Jtag
if (!ret.IsSuccessful) return new(ret.Error); if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed")); else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed"));
var retData = await LoadDRCareOutput(4); var retData = await LoadDRCareOutput(1);
if (!retData.IsSuccessful) if (!retData.IsSuccessful)
{
return new(new Exception("Read Status Reg Failed")); return new(new Exception("Read Status Reg Failed"));
}
return retData.Value; return retData.Value;
} }
@ -901,4 +925,48 @@ public class Jtag
return true; return true;
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="portNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public async ValueTask<Result<BitArray>> BoundaryScan(int portNum)
{
if (portNum <= 0)
return new(new ArgumentException("The number of port couldn't be negative", nameof(portNum)));
// Clear Data
await MsgBus.UDPServer.ClearUDPData(this.address);
logger.Trace($"Clear up udp server {this.address} receive data");
Result<bool> ret;
ret = await CloseTest();
if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
ret = await RunTest();
if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
logger.Trace("Jtag initialize");
ret = await ExecRDCmd(JtagCmd.JTAG_DR_SAMPLE);
if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_JRST Failed"));
var retData = await LoadDRCareOutputArray(((uint)(portNum % 32 == 0 ? portNum / 32 : portNum / 32 + 1)));
if (!retData.IsSuccessful)
return new(new Exception("Read Status Reg Failed"));
ret = await CloseTest();
if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
var byteArray = Common.Number.UInt32ArrayToBytes(retData.Value);
if (!byteArray.IsSuccessful) return new(byteArray.Error);
return new BitArray(byteArray.Value);
}
} }

View File

@ -8,9 +8,6 @@
/* eslint-disable */ /* eslint-disable */
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
import { batchSetConstraintStates, notifyConstraintChange } from './stores/constraints';
import type { ConstraintLevel } from './stores/constraints';
export class Client { export class Client {
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }; private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
private baseUrl: string; private baseUrl: string;
@ -473,7 +470,7 @@ export class JtagClient {
* @param address (optional) * @param address (optional)
* @param port (optional) * @param port (optional)
*/ */
getDeviceIDCode(address: string | undefined, port: number | undefined): Promise<void> { getDeviceIDCode(address: string | undefined, port: number | undefined): Promise<number> {
let url_ = this.baseUrl + "/api/Jtag/GetDeviceIDCode?"; let url_ = this.baseUrl + "/api/Jtag/GetDeviceIDCode?";
if (address === null) if (address === null)
throw new Error("The parameter 'address' cannot be null."); throw new Error("The parameter 'address' cannot be null.");
@ -485,6 +482,61 @@ export class JtagClient {
url_ += "port=" + encodeURIComponent("" + port) + "&"; url_ += "port=" + encodeURIComponent("" + port) + "&";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processGetDeviceIDCode(_response);
});
}
protected processGetDeviceIDCode(response: Response): Promise<number> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 !== undefined ? resultData200 : <any>null;
return result200;
});
} else if (status === 500) {
return response.text().then((_responseText) => {
let result500: any = null;
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result500 = Exception.fromJS(resultData500);
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<number>(null as any);
}
/**
*
* @param address (optional)
* @param port (optional)
*/
readStatusReg(address: string | undefined, port: number | undefined): Promise<void> {
let url_ = this.baseUrl + "/api/Jtag/ReadStatusReg?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
if (port === null)
throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = { let options_: RequestInit = {
method: "GET", method: "GET",
headers: { headers: {
@ -492,11 +544,11 @@ export class JtagClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processGetDeviceIDCode(_response); return this.processReadStatusReg(_response);
}); });
} }
protected processGetDeviceIDCode(response: Response): Promise<void> { protected processReadStatusReg(response: Response): Promise<void> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
@ -520,7 +572,7 @@ export class JtagClient {
* @param address (optional) * @param address (optional)
* @param file (optional) * @param file (optional)
*/ */
uploadBitstream(address: string | undefined, file: FileParameter | null | undefined): Promise<void> { uploadBitstream(address: string | undefined, file: FileParameter | null | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/Jtag/UploadBitstream?"; let url_ = this.baseUrl + "/api/Jtag/UploadBitstream?";
if (address === null) if (address === null)
throw new Error("The parameter 'address' cannot be null."); throw new Error("The parameter 'address' cannot be null.");
@ -536,6 +588,7 @@ export class JtagClient {
body: content_, body: content_,
method: "POST", method: "POST",
headers: { headers: {
"Accept": "application/json"
} }
}; };
@ -544,7 +597,147 @@ export class JtagClient {
}); });
} }
protected processUploadBitstream(response: Response): Promise<void> { protected processUploadBitstream(response: Response): Promise<boolean> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 !== undefined ? resultData200 : <any>null;
return result200;
});
} else if (status === 400) {
return response.text().then((_responseText) => {
let result400: any = null;
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result400 = resultData400 !== undefined ? resultData400 : <any>null;
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<boolean>(null as any);
}
/**
* Jtag下载比特流文件
* @param address (optional)
* @param port (optional)
*/
downloadBitstream(address: string | undefined, port: number | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/Jtag/DownloadBitstream?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
if (port === null)
throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processDownloadBitstream(_response);
});
}
protected processDownloadBitstream(response: Response): Promise<boolean> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 !== undefined ? resultData200 : <any>null;
return result200;
});
} else if (status === 400) {
return response.text().then((_responseText) => {
let result400: any = null;
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result400 = resultData400 !== undefined ? resultData400 : <any>null;
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
});
} else if (status === 500) {
return response.text().then((_responseText) => {
let result500: any = null;
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result500 = Exception.fromJS(resultData500);
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<boolean>(null as any);
}
}
export class RemoteUpdaterClient {
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
private baseUrl: string;
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
this.http = http ? http : window as any;
this.baseUrl = baseUrl ?? "http://localhost:5000";
}
/**
*
* @param address (optional)
* @param goldenBitream (optional)
* @param bitstream1 (optional)
* @param bitstream2 (optional)
* @param bitstream3 (optional)
* @return
*/
uploadBitstreams(address: string | undefined, goldenBitream: FileParameter | null | undefined, bitstream1: FileParameter | null | undefined, bitstream2: FileParameter | null | undefined, bitstream3: FileParameter | null | undefined): Promise<void> {
let url_ = this.baseUrl + "/api/RemoteUpdater/UploadBitstream?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
url_ = url_.replace(/[?&]$/, "");
const content_ = new FormData();
if (goldenBitream !== null && goldenBitream !== undefined)
content_.append("goldenBitream", goldenBitream.data, goldenBitream.fileName ? goldenBitream.fileName : "goldenBitream");
if (bitstream1 !== null && bitstream1 !== undefined)
content_.append("bitstream1", bitstream1.data, bitstream1.fileName ? bitstream1.fileName : "bitstream1");
if (bitstream2 !== null && bitstream2 !== undefined)
content_.append("bitstream2", bitstream2.data, bitstream2.fileName ? bitstream2.fileName : "bitstream2");
if (bitstream3 !== null && bitstream3 !== undefined)
content_.append("bitstream3", bitstream3.data, bitstream3.fileName ? bitstream3.fileName : "bitstream3");
let options_: RequestInit = {
body: content_,
method: "POST",
headers: {
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processUploadBitstreams(_response);
});
}
protected processUploadBitstreams(response: Response): Promise<void> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
@ -567,12 +760,13 @@ export class JtagClient {
} }
/** /**
* Jtag下载比特流文 *
* @param address (optional) * @param address (optional)
* @param port (optional) * @param port (optional)
* @param bitstreamNum (optional)
*/ */
downloadBitstream(address: string | undefined, port: number | undefined): Promise<void> { updateBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<void> {
let url_ = this.baseUrl + "/api/Jtag/DownloadBitstream?"; let url_ = this.baseUrl + "/api/RemoteUpdater/DownloadBitstream?";
if (address === null) if (address === null)
throw new Error("The parameter 'address' cannot be null."); throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined) else if (address !== undefined)
@ -581,6 +775,10 @@ export class JtagClient {
throw new Error("The parameter 'port' cannot be null."); throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined) else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&"; url_ += "port=" + encodeURIComponent("" + port) + "&";
if (bitstreamNum === null)
throw new Error("The parameter 'bitstreamNum' cannot be null.");
else if (bitstreamNum !== undefined)
url_ += "bitstreamNum=" + encodeURIComponent("" + bitstreamNum) + "&";
url_ = url_.replace(/[?&]$/, ""); url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = { let options_: RequestInit = {
@ -590,11 +788,11 @@ export class JtagClient {
}; };
return this.http.fetch(url_, options_).then((_response: Response) => { return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processDownloadBitstream(_response); return this.processUpdateBitstream(_response);
}); });
} }
protected processDownloadBitstream(response: Response): Promise<void> { protected processUpdateBitstream(response: Response): Promise<void> {
const status = response.status; const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); }; let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) { if (status === 200) {
@ -619,6 +817,308 @@ export class JtagClient {
} }
return Promise.resolve<void>(null as any); return Promise.resolve<void>(null as any);
} }
/**
*
* @param address (optional)
* @param port (optional)
* @param bitstreamNum (optional)
* @return
*/
downloadMultiBitstreams(address: string | undefined, port: number | undefined, bitstreamNum: number | null | undefined): Promise<void> {
let url_ = this.baseUrl + "/api/RemoteUpdater/DownloadMultiBitstreams?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
if (port === null)
throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&";
if (bitstreamNum !== undefined && bitstreamNum !== null)
url_ += "bitstreamNum=" + encodeURIComponent("" + bitstreamNum) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processDownloadMultiBitstreams(_response);
});
}
protected processDownloadMultiBitstreams(response: Response): Promise<void> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
return;
});
} else if (status === 400) {
return response.text().then((_responseText) => {
let result400: any = null;
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result400 = ProblemDetails.fromJS(resultData400);
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
});
} else if (status === 500) {
return response.text().then((_responseText) => {
return throwException("A server side error occurred.", status, _responseText, _headers);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<void>(null as any);
}
/**
*
* @param address (optional)
* @param port (optional)
* @param bitstreamNum (optional)
* @return
*/
hotResetBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<void> {
let url_ = this.baseUrl + "/api/RemoteUpdater/HotResetBitstream?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
if (port === null)
throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&";
if (bitstreamNum === null)
throw new Error("The parameter 'bitstreamNum' cannot be null.");
else if (bitstreamNum !== undefined)
url_ += "bitstreamNum=" + encodeURIComponent("" + bitstreamNum) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processHotResetBitstream(_response);
});
}
protected processHotResetBitstream(response: Response): Promise<void> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
return;
});
} else if (status === 400) {
return response.text().then((_responseText) => {
let result400: any = null;
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result400 = ProblemDetails.fromJS(resultData400);
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
});
} else if (status === 500) {
return response.text().then((_responseText) => {
return throwException("A server side error occurred.", status, _responseText, _headers);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<void>(null as any);
}
}
export class DataClient {
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
private baseUrl: string;
protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
this.http = http ? http : window as any;
this.baseUrl = baseUrl ?? "http://localhost:5000";
}
/**
*
* @return
*/
createTables(): Promise<FileResponse> {
let url_ = this.baseUrl + "/api/Data/CreateTable";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processCreateTables(_response);
});
}
protected processCreateTables(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
/**
*
* @return
*/
dropTables(): Promise<FileResponse> {
let url_ = this.baseUrl + "/api/Data/DropTables";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "DELETE",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processDropTables(_response);
});
}
protected processDropTables(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
/**
*
* @return
*/
allUsers(): Promise<FileResponse> {
let url_ = this.baseUrl + "/api/Data/AllUsers";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processAllUsers(_response);
});
}
protected processAllUsers(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
/**
*
* @param name (optional)
* @return
*/
signUpUser(name: string | undefined): Promise<FileResponse> {
let url_ = this.baseUrl + "/api/Data/SignUpUser?";
if (name === null)
throw new Error("The parameter 'name' cannot be null.");
else if (name !== undefined)
url_ += "name=" + encodeURIComponent("" + name) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processSignUpUser(_response);
});
}
protected processSignUpUser(response: Response): Promise<FileResponse> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200 || status === 206) {
const contentDisposition = response.headers ? response.headers.get("content-disposition") : undefined;
let fileNameMatch = contentDisposition ? /filename\*=(?:(\\?['"])(.*?)\1|(?:[^\s]+'.*?')?([^;\n]*))/g.exec(contentDisposition) : undefined;
let fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[3] || fileNameMatch[2] : undefined;
if (fileName) {
fileName = decodeURIComponent(fileName);
} else {
fileNameMatch = contentDisposition ? /filename="?([^"]*?)"?(;|$)/g.exec(contentDisposition) : undefined;
fileName = fileNameMatch && fileNameMatch.length > 1 ? fileNameMatch[1] : undefined;
}
return response.blob().then(blob => { return { fileName: fileName, data: blob, status: status, headers: _headers }; });
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<FileResponse>(null as any);
}
} }
/** Package options which to send address to read or write */ /** Package options which to send address to read or write */
@ -687,8 +1187,8 @@ export interface ISendAddrPackOptions {
/** Package Burst Type */ /** Package Burst Type */
export enum BurstType { export enum BurstType {
ExtendBurst = 0, FixedBurst = 0,
FixedBurst = 1, ExtendBurst = 1,
} }
/** UDP接受数据包格式 */ /** UDP接受数据包格式 */
@ -755,6 +1255,54 @@ export interface IUDPData {
hasRead?: boolean; hasRead?: boolean;
} }
export class Exception implements IException {
message?: string;
innerException?: Exception | undefined;
source?: string | undefined;
stackTrace?: string | undefined;
constructor(data?: IException) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
}
init(_data?: any) {
if (_data) {
this.message = _data["Message"];
this.innerException = _data["InnerException"] ? Exception.fromJS(_data["InnerException"]) : <any>undefined;
this.source = _data["Source"];
this.stackTrace = _data["StackTrace"];
}
}
static fromJS(data: any): Exception {
data = typeof data === 'object' ? data : {};
let result = new Exception();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["Message"] = this.message;
data["InnerException"] = this.innerException ? this.innerException.toJSON() : <any>undefined;
data["Source"] = this.source;
data["StackTrace"] = this.stackTrace;
return data;
}
}
export interface IException {
message?: string;
innerException?: Exception | undefined;
source?: string | undefined;
stackTrace?: string | undefined;
}
export class ProblemDetails implements IProblemDetails { export class ProblemDetails implements IProblemDetails {
type?: string | undefined; type?: string | undefined;
title?: string | undefined; title?: string | undefined;
@ -840,6 +1388,13 @@ export interface FileParameter {
fileName: string; fileName: string;
} }
export interface FileResponse {
data: Blob;
status: number;
fileName?: string;
headers?: { [name: string]: any };
}
export class ApiException extends Error { export class ApiException extends Error {
message: string; message: string;
status: number; status: number;
@ -869,57 +1424,4 @@ function throwException(message: string, status: number, response: string, heade
throw result; throw result;
else else
throw new ApiException(message, status, response, headers, null); throw new ApiException(message, status, response, headers, null);
} }
// 约束通信相关方法
export function receiveConstraintUpdates(constraints: Record<string, ConstraintLevel>) {
// 批量更新约束状态
batchSetConstraintStates(constraints);
}
export function sendConstraintUpdate(constraint: string, level: ConstraintLevel) {
// 向后端发送约束状态变化
console.log(`发送约束 ${constraint} 状态变化为 ${level}`);
// TODO: 实际的WebSocket或HTTP请求发送约束变化
// 例如:
// socket.emit('constraintUpdate', { constraint, level });
// 或
// fetch('/api/constraints', {
// method: 'POST',
// body: JSON.stringify({ constraint, level }),
// headers: { 'Content-Type': 'application/json' }
// });
}
// 初始化约束通信
export function initConstraintCommunication() {
// 监听服务器发来的约束状态变化
// 示例:
// socket.on('constraintUpdates', (data) => {
// receiveConstraintUpdates(data);
// });
// 模拟接收一些初始约束状态
setTimeout(() => {
receiveConstraintUpdates({
'A1': 'high',
'A2': 'low',
'A3': 'undefined'
});
}, 1000);
}
// 覆盖全局notifyConstraintChange加入发送逻辑
const originalNotifyConstraintChange = notifyConstraintChange;
const wrappedNotifyConstraintChange = (constraint: string, level: ConstraintLevel) => {
// 调用原始方法更新本地状态
originalNotifyConstraintChange(constraint, level);
// 向后端发送更新
sendConstraintUpdate(constraint, level);
};
// 替换全局方法
(window as any).__notifyConstraintChange = notifyConstraintChange;
(window as any).notifyConstraintChange = wrappedNotifyConstraintChange;

View File

@ -1,26 +1,34 @@
<script setup lang="ts"> <script setup lang="ts">
import Navbar from "./components/Navbar.vue"; import Navbar from "./components/Navbar.vue";
import Dialog from "./components/Dialog.vue";
import { ref, provide, onMounted } from "vue"; import { ref, provide, onMounted } from "vue";
// //
const isDarkMode = ref(window.matchMedia('(prefers-color-scheme: dark)').matches); const isDarkMode = ref(
window.matchMedia("(prefers-color-scheme: dark)").matches,
);
// //
onMounted(() => { onMounted(() => {
// //
applyTheme(); applyTheme();
// //
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { window
// .matchMedia("(prefers-color-scheme: dark)")
isDarkMode.value = e.matches; .addEventListener("change", (e) => {
applyTheme(); //
}); isDarkMode.value = e.matches;
applyTheme();
});
}); });
// //
const applyTheme = () => { const applyTheme = () => {
document.documentElement.setAttribute('data-theme', isDarkMode.value ? 'night' : 'winter'); document.documentElement.setAttribute(
"data-theme",
isDarkMode.value ? "night" : "winter",
);
}; };
// //
@ -30,9 +38,9 @@ const toggleTheme = () => {
}; };
// //
provide('theme', { provide("theme", {
isDarkMode, isDarkMode,
toggleTheme toggleTheme,
}); });
</script> </script>
@ -40,15 +48,18 @@ provide('theme', {
<div> <div>
<header class="relative"> <header class="relative">
<Navbar></Navbar> <Navbar></Navbar>
<Dialog></Dialog>
</header> </header>
<main> <main>
<RouterView /> <RouterView />
</main> <footer class="footer footer-center p-4 bg-base-300 text-base-content"> </main>
<footer class="footer footer-center p-4 bg-base-300 text-base-content">
<div> <div>
<p>Copyright © 2023 - All right reserved by OurEDA</p> <p>Copyright © 2023 - All right reserved by OurEDA</p>
</div> </div>
</footer> </div> </footer>
</div>
</template> </template>
<style scoped> <style scoped>

20
src/components/Dialog.vue Normal file
View File

@ -0,0 +1,20 @@
<template>
<dialog class="modal" :open="dialog.isDialogOpen">
<div class="modal-box w-2/5 max-w-md max-h-xs">
<h3 class="text-lg font-bold">{{ dialog.dialogTitle }}</h3>
<p class="py-4">{{ dialog.dialogContent }}</p>
<div class="modal-action">
<button class="btn" @click="dialog.closeDialog">Close</button>
</div>
</div>
</dialog>
</template>
<script lang="ts" setup>
import { useDialogStore } from "@/stores/dialog";
const dialog = useDialogStore();
</script>
<style scoped lang="postcss">
@import "@/assets/main.css";
</style>

View File

@ -5,19 +5,19 @@
<div class="flex flex-col w-full h-full"> <div class="flex flex-col w-full h-full">
<label class="input w-full my-3"> <label class="input w-full my-3">
<img class="h-[1em] opacity-50" src="@/assets/user.svg" alt="User img" /> <img class="h-[1em] opacity-50" src="@/assets/user.svg" alt="User img" />
<input type="text" class="grow" placeholder="User Name" /> <input type="text" class="grow" placeholder="用户名" />
</label> </label>
<label class="input w-full my-3"> <label class="input w-full my-3">
<img class="h-[1em] opacity-50" src="@/assets/pwd.svg" alt="User img" /> <img class="h-[1em] opacity-50" src="@/assets/pwd.svg" alt="User img" />
<input type="text" class="grow" placeholder="User Name" /> <input type="text" class="grow" placeholder="密码" />
</label> </label>
</div> </div>
<div> <div>
<RouterLink class="flex justify-end mx-3" to="/">Forget Password</RouterLink> <RouterLink class="flex justify-end mx-3" to="/">忘记密码?</RouterLink>
</div> </div>
<div class="card-actions flex items-end my-3"> <div class="card-actions flex items-end my-3">
<button class="btn flex-1">Sign Up</button> <button class="btn flex-1">注册</button>
<button class="btn btn-primary flex-3">Login</button> <button class="btn btn-primary flex-3">登录</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -2,24 +2,28 @@
<div class="navbar bg-base-100 shadow-xl"> <div class="navbar bg-base-100 shadow-xl">
<div class="navbar-start"> <div class="navbar-start">
<div class="dropdown"> <div class="dropdown">
<div tabindex="0" role="button" class="btn btn-ghost hover:bg-primary hover:bg-opacity-20 transition-all duration-300"> <div tabindex="0" role="button"
class="btn btn-ghost hover:bg-primary hover:bg-opacity-20 transition-all duration-300">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
</svg> </svg>
</div> </div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-lg z-50 mt-3 w-52 p-2 shadow-lg transition-all duration-300 ease-in-out"> <ul tabindex="0"
class="menu menu-sm dropdown-content bg-base-100 rounded-lg z-50 mt-3 w-52 p-2 shadow-lg transition-all duration-300 ease-in-out">
<li class="my-1 hover:translate-x-1 transition-all duration-300"> <li class="my-1 hover:translate-x-1 transition-all duration-300">
<router-link to="/" class="text-base font-medium"> <router-link to="/" class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> stroke="currentColor" stroke-width="2">
<polyline points="9 22 9 12 15 12 15 22"/> <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
<polyline points="9 22 9 12 15 12 15 22" />
</svg> </svg>
首页 首页
</router-link> </router-link>
</li> </li>
<li class="my-1 hover:translate-x-1 transition-all duration-300"> <li class="my-1 hover:translate-x-1 transition-all duration-300">
<router-link to="/user" class="text-base font-medium"> <router-link to="/user" class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle> <circle cx="12" cy="7" r="4"></circle>
</svg> </svg>
@ -28,7 +32,8 @@
</li> </li>
<li class="my-1 hover:translate-x-1 transition-all duration-300"> <li class="my-1 hover:translate-x-1 transition-all duration-300">
<router-link to="/project" class="text-base font-medium"> <router-link to="/project" class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path> <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path> <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
</svg> </svg>
@ -37,7 +42,8 @@
</li> </li>
<li class="my-1 hover:translate-x-1 transition-all duration-300"> <li class="my-1 hover:translate-x-1 transition-all duration-300">
<router-link to="/test" class="text-base font-medium"> <router-link to="/test" class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path> <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path>
</svg> </svg>
测试功能 测试功能
@ -45,7 +51,8 @@
</li> </li>
<li class="my-1 hover:translate-x-1 transition-all duration-300"> <li class="my-1 hover:translate-x-1 transition-all duration-300">
<router-link to="/test/jtag" class="text-base font-medium"> <router-link to="/test/jtag" class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect> <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line> <line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line> <line x1="12" y1="17" x2="12" y2="21"></line>
@ -53,6 +60,17 @@
JTAG测试 JTAG测试
</router-link> </router-link>
</li> </li>
<li class="my-1 hover:translate-x-1 transition-all duration-300">
<a href="http://localhost:5000/swagger" target="_self" rel="noopener noreferrer"
class="text-base font-medium">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 opacity-70" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M3 3h18v18H3z"></path>
<path d="M8 8h8v8H8z" fill="currentColor"></path>
</svg>
OpenAPI文档
</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -62,7 +80,8 @@
</router-link> </router-link>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<router-link to="/login" class="btn btn-primary text-base-100 transition-all duration-300 hover:scale-105 hover:shadow-lg mr-3"> <router-link to="/login"
class="btn btn-primary text-base-100 transition-all duration-300 hover:scale-105 hover:shadow-lg mr-3">
登录 登录
</router-link> </router-link>
<div class="ml-2 transition-all duration-500 hover:rotate-12"> <div class="ml-2 transition-all duration-500 hover:rotate-12">

View File

@ -1,48 +1,103 @@
<template> <template>
<div class="card card-dash shadow-xl w-90 h-60"> <div class="flex flex-col bg-base-100 justify-center items-center">
<div class="card-body flex"> <!-- Title -->
<!-- Title --> <h1 class="font-bold text-2xl">上传比特流文件</h1>
<h1 class="card-title place-self-center font-bold text-2xl">
上传比特流文件
</h1>
<!-- Input File --> <!-- Input File -->
<fieldset class="fieldset w-full"> <fieldset class="fieldset w-full">
<legend class="fieldset-legend text-sm">选择或拖拽上传文件</legend> <legend class="fieldset-legend text-sm">选择或拖拽上传文件</legend>
<input type="file" class="file-input" @change="handleFileChange" /> <input type="file" class="file-input w-full" @change="handleFileChange" />
<label class="fieldset-label">文件最大容量: 2MB</label> <label class="fieldset-label">文件最大容量: {{ maxMemory }}MB</label>
</fieldset> </fieldset>
<!-- Upload Button --> <!-- Upload Button -->
<div class="card-actions"> <div class="card-actions w-full">
<button @click="uploadBitStream" class="btn btn-primary grow"> <button @click="handleClick" class="btn btn-primary grow">
上传 {{ buttonText }}
</button> </button>
</div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
var bitstream = null; import { computed } from "vue";
import { useDialogStore } from "@/stores/dialog";
import { isNull, isUndefined } from "lodash";
const dialog = useDialogStore();
const buttonText = computed(() => {
return isUndefined(props.downloadEvent) ? "上传" : "上传并下载";
});
var bitstream: File | null = null;
function handleFileChange(event: Event): void { function handleFileChange(event: Event): void {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
const file = target.files?.[0]; // const file = target.files?.[0]; //
if (!file) { if (!file) {
console.error("未选择文件");
return; return;
} }
bitstream = file; bitstream = file;
console.log(bitstream);
} }
async function uploadBitStream() {} function checkFile(file: File | null): boolean {
if (isNull(file)) {
dialog.error(`未选择文件`);
return false;
}
function checkFileType(file: File) {} const maxBytes = props.maxMemory! * 1024 * 1024; // MB
if (file.size > maxBytes) {
dialog.error(`文件大小超过最大限制: ${props.maxMemory}MB`);
return false;
}
return true;
}
async function handleClick(event: Event): Promise<void> {
if (!checkFile(bitstream)) return;
if (isUndefined(props.uploadEvent)) {
dialog.error("无法上传");
return;
}
// Upload
try {
const ret = await props.uploadEvent(event, bitstream);
if (isUndefined(props.downloadEvent)) {
if (ret) dialog.info("上传成功");
else dialog.error("上传失败");
return;
}
} catch (e) {
dialog.error("上传失败");
console.error(e);
return;
}
// Download
try {
const ret = await props.downloadEvent();
if (ret) dialog.info("下载成功");
else dialog.error("下载失败");
} catch (e) {
dialog.error("下载失败");
console.error(e);
}
}
interface Props {
uploadEvent?: Function;
downloadEvent?: Function;
maxMemory?: number;
}
const props = withDefaults(defineProps<Props>(), {
maxMemory: 4,
});
</script> </script>
<style scoped lang="postcss"> <style scoped lang="postcss">

View File

@ -5,10 +5,10 @@ import { createPinia } from 'pinia'
import App from '@/App.vue' import App from '@/App.vue'
import router from './router' import router from './router'
import { initConstraintCommunication } from './APIClient' // import { Client } from './APIClient'
const app = createApp(App).use(router).use(createPinia()).mount('#app') const app = createApp(App).use(router).use(createPinia()).mount('#app')
// 初始化约束通信 // 初始化约束通信
initConstraintCommunication(); // initConstraintCommunication();

View File

@ -11,8 +11,8 @@ const routes = [
{ path: "/login", name: "Login", component: LoginView }, { path: "/login", name: "Login", component: LoginView },
{ path: "/user", name: "User", component: UserView }, { path: "/user", name: "User", component: UserView },
{ path: "/test", name: "Test", component: TestView }, { path: "/test", name: "Test", component: TestView },
{ path: "/test/jtag", name:"JtagTest", component: JtagTest}, { path: "/test/jtag", name: "JtagTest", component: JtagTest },
{ path: "/project", name: "Project", component: ProjectView } // 新增工程界面 { path: "/project", name: "Project", component: ProjectView },
]; ];
const router = createRouter({ const router = createRouter({

45
src/stores/dialog.ts Normal file
View File

@ -0,0 +1,45 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { isUndefined } from 'lodash';
export const useDialogStore = defineStore('dialog', () => {
type Title = "Error" | "Info" | "Warn";
const isDialogOpen = ref(false);
const dialogTitle = ref<Title>("Error");
const dialogContent = ref("这是一个错误");
function openDialog(title: Title, content?: string) {
if (!isUndefined(content) && content.length != 0)
dialogContent.value = content;
dialogTitle.value = title;
isDialogOpen.value = true;
}
function closeDialog() {
isDialogOpen.value = false;
}
function info(content?: string) {
openDialog("Info", content);
}
function error(content?: string) {
openDialog("Error", content);
}
function warn(content?: string) {
openDialog("Warn", content);
}
return {
isDialogOpen,
dialogTitle,
dialogContent,
openDialog,
closeDialog,
info,
error,
warn
}
})

View File

@ -1,17 +1,107 @@
<template> <template>
<div class="h-screen overflow-hidden"> <div class="flex w-screen h-screen justify-center">
<Switch width="1400" height="360" /> <div class="flex flex-col w-3/5 h-screen shadow-2xl p-10">
<MechanicalButton width="1400" height="360" /> <div class="flex justify-center">
<PopButton></PopButton> <h1 class="font-bold text-3xl">Jtag 下载</h1>
</div>
<div class="divider"></div>
<div class="w-full">
<div class="collapse bg-primary border-base-300 border">
<input type="checkbox" />
<div class="collapse-title font-semibold text-lg text-white">
自定义开发板参数
</div>
<div class="collapse-content bg-primary-content text-sm">
<div class="form-control w-full my-3">
<label class="label">
<span class="label-text text-gray-700">开发板IP地址</span>
</label>
<label class="input w-full">
<img class="h-[1em] opacity-50" src="@/assets/pwd.svg" alt="User img" />
<input type="text" class="grow" placeholder="IP地址" v-model="boardAddress" />
</label>
</div>
<div class="form-control w-full my-3">
<label class="label">
<span class="label-text text-gray-700">开发板端口号</span>
</label>
<label class="input w-full">
<img class="h-[1em] opacity-50" src="@/assets/pwd.svg" alt="User img" />
<input type="text" class="grow" placeholder="端口号" v-model="boardPort" />
</label>
</div>
</div>
</div>
</div>
<div class="divider"></div>
<UploadCard :upload-event="uploadBitstream" :download-event="downloadBitstream">
</UploadCard>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import PopButton from "@/components/PopButton.vue"; import { JtagClient, type FileParameter } from "@/APIClient";
import MechanicalButton from "@/components/equipments/MechanicalButton.vue"; import UploadCard from "@/components/UploadCard.vue";
import Switch from "@/components/equipments/Switch.vue"; import { useDialogStore } from "@/stores/dialog";
import { toNumber } from "lodash";
import { ref } from "vue";
const jtagController = new JtagClient();
const dialog = useDialogStore();
// Models with default values
const boardAddress = ref("127.0.0.1"); // IP
const boardPort = ref("1234"); //
async function uploadBitstream(event: Event, bitstream: File) {
if (boardAddress.value.length == 0) {
dialog.error("开发板地址空缺");
return;
}
if (boardPort.value.length == 0) {
dialog.error("开发板端口空缺");
return;
}
const fileParam: FileParameter = {
data: bitstream,
fileName: bitstream.name,
};
try {
const resp = await jtagController.uploadBitstream(
boardAddress.value,
fileParam,
);
return resp;
} catch (e) {
dialog.error("上传错误");
}
}
async function downloadBitstream() {
if (boardAddress.value.length == 0) {
dialog.error("开发板地址空缺");
return;
}
if (boardPort.value.length == 0) {
dialog.error("开发板端口空缺");
return;
}
try {
const resp = await jtagController.downloadBitstream(
boardAddress.value,
toNumber(boardPort.value),
);
return resp;
} catch (e) {
dialog.error("上传错误");
console.error(e);
}
}
</script> </script>
<style scoped lang="postcss"> <style scoped lang="postcss">
@import "../assets/main.css"; @import "../assets/main.css";
</style> </style>

View File

@ -6,6 +6,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools' import vueDevTools from 'vite-plugin-vue-devtools'
import tailwindcss from '@tailwindcss/postcss' import tailwindcss from '@tailwindcss/postcss'
import autoprefixer from 'autoprefixer' import autoprefixer from 'autoprefixer'
import basicSsl from '@vitejs/plugin-basic-ssl'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
@ -20,6 +21,7 @@ export default defineConfig({
}), }),
vueJsx(), vueJsx(),
vueDevTools(), vueDevTools(),
// basicSsl()
], ],
resolve: { resolve: {
alias: { alias: {
@ -33,5 +35,18 @@ export default defineConfig({
autoprefixer() autoprefixer()
] ]
} }
},
build: {
outDir: 'wwwroot',
emptyOutDir: true, // also necessary
},
server: {
proxy: {
"/swagger": {
target: 'http://localhost:5000',
changeOrigin: true
}
},
port: 5173,
} }
}) })