feat: add remote update function
This commit is contained in:
parent
48ae3b5975
commit
00ce79fa7b
|
@ -418,8 +418,9 @@ public class RemoteUpdater : ControllerBase
|
|||
/// <param name="bitstream3">比特流文件3</param>
|
||||
/// <returns>上传结果</returns>
|
||||
[HttpPost("UploadBitstream")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
|
||||
public async ValueTask<IResult> UploadBitstreams(
|
||||
string address,
|
||||
IFormFile? goldenBitream,
|
||||
|
@ -469,7 +470,7 @@ public class RemoteUpdater : ControllerBase
|
|||
}
|
||||
|
||||
logger.Info($"Device {address} Upload Bitstream Successfully");
|
||||
return TypedResults.Ok("Bitstream Upload Successfully");
|
||||
return TypedResults.Ok(true);
|
||||
}
|
||||
|
||||
private async ValueTask<Result<byte[]>> ProcessBitstream(string filePath)
|
||||
|
@ -522,9 +523,10 @@ public class RemoteUpdater : ControllerBase
|
|||
/// <param name="port"> 设备端口 </param>
|
||||
/// <param name="bitstreamNum"> 比特流位号 </param>
|
||||
[HttpPost("DownloadBitstream")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[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)
|
||||
{
|
||||
// 检查文件
|
||||
|
@ -571,9 +573,10 @@ public class RemoteUpdater : ControllerBase
|
|||
/// <param name="bitstreamNum">比特流编号</param>
|
||||
/// <returns>总共上传比特流的数量</returns>
|
||||
[HttpPost("DownloadMultiBitstreams")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[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)
|
||||
{
|
||||
// 检查文件
|
||||
|
@ -630,9 +633,10 @@ public class RemoteUpdater : ControllerBase
|
|||
/// <param name="bitstreamNum">比特流编号</param>
|
||||
/// <returns>操作结果</returns>
|
||||
[HttpPost("HotResetBitstream")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[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 RemoteUpdate.RemoteUpdateClient(address, port);
|
||||
|
@ -640,7 +644,7 @@ public class RemoteUpdater : ControllerBase
|
|||
|
||||
if (ret.IsSuccessful)
|
||||
{
|
||||
logger.Info($"Device {address}即可 Update bitstream successfully");
|
||||
logger.Info($"Device {address} Update bitstream successfully");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
else
|
||||
|
@ -649,6 +653,35 @@ public class RemoteUpdater : ControllerBase
|
|||
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 RemoteUpdate.RemoteUpdateClient(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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -532,4 +532,23 @@ public class RemoteUpdateClient
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<UInt32>> GetVersion()
|
||||
{
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address);
|
||||
logger.Trace("Clear udp data finished");
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdateClientAddr.Version, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
|
||||
var retData = ret.Value.Options.Data;
|
||||
if (retData is null || retData.Length != 4) return new(new Exception("Failed to read remote update firmware version"));
|
||||
var version = Common.Number.BytesToUInt32(retData);
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
409
src/APIClient.ts
409
src/APIClient.ts
|
@ -8,47 +8,6 @@
|
|||
/* eslint-disable */
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
export class Client {
|
||||
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";
|
||||
}
|
||||
|
||||
get(): Promise<void> {
|
||||
let url_ = this.baseUrl + "/";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processGet(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGet(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 !== 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 UDPClient {
|
||||
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
|
||||
private baseUrl: string;
|
||||
|
@ -408,63 +367,6 @@ export class JtagClient {
|
|||
return Promise.resolve<string>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一个Jtag命令
|
||||
* @param address (optional) 设备地址
|
||||
* @param port (optional) 设备端口
|
||||
* @param hexDevAddr (optional) 16进制设备目的地址(Jtag)
|
||||
* @param hexCmd (optional) 16进制命令
|
||||
*/
|
||||
runCommand(address: string | undefined, port: number | undefined, hexDevAddr: string | undefined, hexCmd: string | undefined): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/Jtag/RunCommand?";
|
||||
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 (hexDevAddr === null)
|
||||
throw new Error("The parameter 'hexDevAddr' cannot be null.");
|
||||
else if (hexDevAddr !== undefined)
|
||||
url_ += "hexDevAddr=" + encodeURIComponent("" + hexDevAddr) + "&";
|
||||
if (hexCmd === null)
|
||||
throw new Error("The parameter 'hexCmd' cannot be null.");
|
||||
else if (hexCmd !== undefined)
|
||||
url_ += "hexCmd=" + encodeURIComponent("" + hexCmd) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processRunCommand(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processRunCommand(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 === 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Jtag ID Code
|
||||
* @param address (optional) 设备地址
|
||||
|
@ -686,6 +588,75 @@ export class JtagClient {
|
|||
}
|
||||
return Promise.resolve<boolean>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* [TODO:description]
|
||||
* @param address (optional) [TODO:parameter]
|
||||
* @param port (optional) [TODO:parameter]
|
||||
* @param portNum (optional) [TODO:parameter]
|
||||
* @return [TODO:return]
|
||||
*/
|
||||
boundaryScan(address: string | undefined, port: number | undefined, portNum: number | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/api/Jtag/BoundaryScan?";
|
||||
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 (portNum === null)
|
||||
throw new Error("The parameter 'portNum' cannot be null.");
|
||||
else if (portNum !== undefined)
|
||||
url_ += "portNum=" + encodeURIComponent("" + portNum) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processBoundaryScan(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processBoundaryScan(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 {
|
||||
|
@ -707,7 +678,7 @@ export class RemoteUpdaterClient {
|
|||
* @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> {
|
||||
uploadBitstreams(address: string | undefined, goldenBitream: FileParameter | null | undefined, bitstream1: FileParameter | null | undefined, bitstream2: FileParameter | null | undefined, bitstream3: FileParameter | null | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/api/RemoteUpdater/UploadBitstream?";
|
||||
if (address === null)
|
||||
throw new Error("The parameter 'address' cannot be null.");
|
||||
|
@ -729,6 +700,7 @@ export class RemoteUpdaterClient {
|
|||
body: content_,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -737,18 +709,22 @@ export class RemoteUpdaterClient {
|
|||
});
|
||||
}
|
||||
|
||||
protected processUploadBitstreams(response: Response): Promise<void> {
|
||||
protected processUploadBitstreams(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) => {
|
||||
return;
|
||||
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 = ProblemDetails.fromJS(resultData400);
|
||||
result400 = ArgumentException.fromJS(resultData400);
|
||||
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
|
@ -756,7 +732,7 @@ export class RemoteUpdaterClient {
|
|||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(null as any);
|
||||
return Promise.resolve<boolean>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -765,7 +741,7 @@ export class RemoteUpdaterClient {
|
|||
* @param port (optional) 设备端口
|
||||
* @param bitstreamNum (optional) 比特流位号
|
||||
*/
|
||||
updateBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<void> {
|
||||
updateBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/api/RemoteUpdater/DownloadBitstream?";
|
||||
if (address === null)
|
||||
throw new Error("The parameter 'address' cannot be null.");
|
||||
|
@ -784,6 +760,7 @@ export class RemoteUpdaterClient {
|
|||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -792,30 +769,37 @@ export class RemoteUpdaterClient {
|
|||
});
|
||||
}
|
||||
|
||||
protected processUpdateBitstream(response: Response): Promise<void> {
|
||||
protected processUpdateBitstream(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) => {
|
||||
return;
|
||||
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 = ProblemDetails.fromJS(resultData400);
|
||||
result400 = ArgumentException.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);
|
||||
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<void>(null as any);
|
||||
return Promise.resolve<boolean>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -825,7 +809,7 @@ export class RemoteUpdaterClient {
|
|||
* @param bitstreamNum (optional) 比特流编号
|
||||
* @return 总共上传比特流的数量
|
||||
*/
|
||||
downloadMultiBitstreams(address: string | undefined, port: number | undefined, bitstreamNum: number | null | undefined): Promise<void> {
|
||||
downloadMultiBitstreams(address: string | undefined, port: number | undefined, bitstreamNum: number | null | undefined): Promise<number> {
|
||||
let url_ = this.baseUrl + "/api/RemoteUpdater/DownloadMultiBitstreams?";
|
||||
if (address === null)
|
||||
throw new Error("The parameter 'address' cannot be null.");
|
||||
|
@ -842,6 +826,7 @@ export class RemoteUpdaterClient {
|
|||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -850,30 +835,37 @@ export class RemoteUpdaterClient {
|
|||
});
|
||||
}
|
||||
|
||||
protected processDownloadMultiBitstreams(response: Response): Promise<void> {
|
||||
protected processDownloadMultiBitstreams(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) => {
|
||||
return;
|
||||
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 = ProblemDetails.fromJS(resultData400);
|
||||
result400 = ArgumentException.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);
|
||||
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<void>(null as any);
|
||||
return Promise.resolve<number>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -883,7 +875,7 @@ export class RemoteUpdaterClient {
|
|||
* @param bitstreamNum (optional) 比特流编号
|
||||
* @return 操作结果
|
||||
*/
|
||||
hotResetBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<void> {
|
||||
hotResetBitstream(address: string | undefined, port: number | undefined, bitstreamNum: number | undefined): Promise<boolean> {
|
||||
let url_ = this.baseUrl + "/api/RemoteUpdater/HotResetBitstream?";
|
||||
if (address === null)
|
||||
throw new Error("The parameter 'address' cannot be null.");
|
||||
|
@ -902,6 +894,7 @@ export class RemoteUpdaterClient {
|
|||
let options_: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -910,30 +903,100 @@ export class RemoteUpdaterClient {
|
|||
});
|
||||
}
|
||||
|
||||
protected processHotResetBitstream(response: Response): Promise<void> {
|
||||
protected processHotResetBitstream(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) => {
|
||||
return;
|
||||
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 = ProblemDetails.fromJS(resultData400);
|
||||
result400 = ArgumentException.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);
|
||||
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<void>(null as any);
|
||||
return Promise.resolve<boolean>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* [TODO:description]
|
||||
* @param address (optional) [TODO:parameter]
|
||||
* @param port (optional) [TODO:parameter]
|
||||
* @return [TODO:return]
|
||||
*/
|
||||
getFirmwareVersion(address: string | undefined, port: number | undefined): Promise<number> {
|
||||
let url_ = this.baseUrl + "/api/RemoteUpdater/GetFirmwareVersion?";
|
||||
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.processGetFirmwareVersion(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processGetFirmwareVersion(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 === 400) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result400: any = null;
|
||||
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
|
||||
result400 = ArgumentException.fromJS(resultData400);
|
||||
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<number>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1303,84 +1366,68 @@ export interface IException {
|
|||
stackTrace?: string | undefined;
|
||||
}
|
||||
|
||||
export class ProblemDetails implements IProblemDetails {
|
||||
type?: string | undefined;
|
||||
title?: string | undefined;
|
||||
status?: number | undefined;
|
||||
detail?: string | undefined;
|
||||
instance?: string | undefined;
|
||||
extensions?: { [key: string]: any; };
|
||||
export class SystemException extends Exception implements ISystemException {
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
constructor(data?: IProblemDetails) {
|
||||
if (data) {
|
||||
for (var property in data) {
|
||||
if (data.hasOwnProperty(property))
|
||||
(<any>this)[property] = (<any>data)[property];
|
||||
}
|
||||
}
|
||||
constructor(data?: ISystemException) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
if (_data) {
|
||||
for (var property in _data) {
|
||||
if (_data.hasOwnProperty(property))
|
||||
this[property] = _data[property];
|
||||
}
|
||||
this.type = _data["type"];
|
||||
this.title = _data["title"];
|
||||
this.status = _data["status"];
|
||||
this.detail = _data["detail"];
|
||||
this.instance = _data["instance"];
|
||||
if (_data["extensions"]) {
|
||||
this.extensions = {} as any;
|
||||
for (let key in _data["extensions"]) {
|
||||
if (_data["extensions"].hasOwnProperty(key))
|
||||
(<any>this.extensions)![key] = _data["extensions"][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
super.init(_data);
|
||||
}
|
||||
|
||||
static fromJS(data: any): ProblemDetails {
|
||||
static fromJS(data: any): SystemException {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new ProblemDetails();
|
||||
let result = new SystemException();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
for (var property in this) {
|
||||
if (this.hasOwnProperty(property))
|
||||
data[property] = this[property];
|
||||
}
|
||||
data["type"] = this.type;
|
||||
data["title"] = this.title;
|
||||
data["status"] = this.status;
|
||||
data["detail"] = this.detail;
|
||||
data["instance"] = this.instance;
|
||||
if (this.extensions) {
|
||||
data["extensions"] = {};
|
||||
for (let key in this.extensions) {
|
||||
if (this.extensions.hasOwnProperty(key))
|
||||
(<any>data["extensions"])[key] = (<any>this.extensions)[key];
|
||||
}
|
||||
}
|
||||
super.toJSON(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IProblemDetails {
|
||||
type?: string | undefined;
|
||||
title?: string | undefined;
|
||||
status?: number | undefined;
|
||||
detail?: string | undefined;
|
||||
instance?: string | undefined;
|
||||
extensions?: { [key: string]: any; };
|
||||
export interface ISystemException extends IException {
|
||||
}
|
||||
|
||||
[key: string]: any;
|
||||
export class ArgumentException extends SystemException implements IArgumentException {
|
||||
message?: string;
|
||||
paramName?: string | undefined;
|
||||
|
||||
constructor(data?: IArgumentException) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
init(_data?: any) {
|
||||
super.init(_data);
|
||||
if (_data) {
|
||||
this.message = _data["Message"];
|
||||
this.paramName = _data["ParamName"];
|
||||
}
|
||||
}
|
||||
|
||||
static fromJS(data: any): ArgumentException {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
let result = new ArgumentException();
|
||||
result.init(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON(data?: any) {
|
||||
data = typeof data === 'object' ? data : {};
|
||||
data["Message"] = this.message;
|
||||
data["ParamName"] = this.paramName;
|
||||
super.toJSON(data);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IArgumentException extends ISystemException {
|
||||
message?: string;
|
||||
paramName?: string | undefined;
|
||||
}
|
||||
|
||||
export interface FileParameter {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { type FileParameter } from "./APIClient";
|
||||
import { isNull, isUndefined } from "lodash";
|
||||
|
||||
export namespace Common {
|
||||
export function toFileParameter(object: File): FileParameter {
|
||||
return {
|
||||
data: object,
|
||||
fileName: object.name
|
||||
}
|
||||
}
|
||||
|
||||
export function toFileParameterOrNull(object?: File | null): FileParameter | null {
|
||||
if (isNull(object) || isUndefined(object)) return null;
|
||||
else return {
|
||||
data: object,
|
||||
fileName: object.name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,15 @@
|
|||
<template>
|
||||
<div class="bg-base-200 min-h-screen p-6">
|
||||
<div class="flex flex-row align-middle">
|
||||
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
|
||||
<button class="btn btn-ghost self-end" @click="
|
||||
() => {
|
||||
isEditMode = !isEditMode;
|
||||
}
|
||||
">
|
||||
编辑
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
|
@ -14,10 +23,10 @@
|
|||
<th>IP 地址</th>
|
||||
<th>版本号</th>
|
||||
<th>默认启动位流</th>
|
||||
<th>黄金位流</th>
|
||||
<th>应用位流1</th>
|
||||
<th>应用位流2</th>
|
||||
<th>应用位流3</th>
|
||||
<th class="w-80">黄金位流</th>
|
||||
<th class="w-80">应用位流1</th>
|
||||
<th class="w-80">应用位流2</th>
|
||||
<th class="w-80">应用位流3</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -25,10 +34,13 @@
|
|||
<!-- 表格内容 -->
|
||||
<tbody>
|
||||
<tr class="hover">
|
||||
<td class="font-medium">192.168.1.100</td>
|
||||
<td class="font-medium">
|
||||
<input v-if="isEditMode" type="text" placeholder="Type here" class="input" />
|
||||
<span v-else>{{ devAddr }}</span>
|
||||
</td>
|
||||
<td>v1.2.3</td>
|
||||
<td>
|
||||
<select class="select select-bordered w-full max-w-xs">
|
||||
<select class="select select-bordered w-full max-w-xs" v-model="selectBitstream">
|
||||
<option selected>黄金位流</option>
|
||||
<option>应用位流1</option>
|
||||
<option>应用位流2</option>
|
||||
|
@ -38,56 +50,50 @@
|
|||
<!-- 黄金位流上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<label class="btn btn-outline btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
<input type="file" class="hidden" @change="handleFileChange($event, goldBitstreamFile)" />
|
||||
</label>
|
||||
<span class="text-xs text-primary truncate max-w-32" v-if="goldBitstreamFile">{{ goldBitstreamFile }}</span>
|
||||
<input type="file" class="file-input file-input-primary"
|
||||
@change="handleFileChange($event, goldBitstreamFile)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流1上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<label class="btn btn-outline btn-secondary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream1File)" />
|
||||
</label>
|
||||
<span class="text-xs text-secondary truncate max-w-32" v-if="appBitstream1File">{{ appBitstream1File }}</span>
|
||||
<input type="file" class="file-input file-input-secondary"
|
||||
@change="handleFileChange($event, appBitstream1File)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流2上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<label class="btn btn-outline btn-accent">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream2File)" />
|
||||
</label>
|
||||
<span class="text-xs text-accent truncate max-w-32" v-if="appBitstream2File">{{ appBitstream2File }}</span>
|
||||
<input type="file" class="file-input file-input-accent"
|
||||
@change="handleFileChange($event, appBitstream2File)" />
|
||||
</div>
|
||||
</td>
|
||||
<!-- 应用位流3上传区 -->
|
||||
<td>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<label class="btn btn-outline btn-info">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
<input type="file" class="hidden" @change="handleFileChange($event, appBitstream3File)" />
|
||||
</label>
|
||||
<span class="text-xs text-info truncate max-w-32" v-if="appBitstream3File">{{ appBitstream3File }}</span>
|
||||
<input type="file" class="file-input file-input-info"
|
||||
@change="handleFileChange($event, appBitstream3File)" />
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<td class="flex gap-2">
|
||||
<button class="btn btn-sm btn-warning">固化</button>
|
||||
<button class="btn btn-sm btn-success">切换并热启动</button>
|
||||
<button class="btn grow btn-warning" @click="
|
||||
uploadAndDownloadBitstreams(
|
||||
devAddr,
|
||||
goldBitstreamFile,
|
||||
appBitstream1File,
|
||||
appBitstream2File,
|
||||
appBitstream3File,
|
||||
)
|
||||
">
|
||||
固化
|
||||
</button>
|
||||
<button class="btn grow btn-success" @click="
|
||||
hotresetBitstream(devAddr, getSelectedBitstreamNum())
|
||||
">
|
||||
切换并热启动
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -96,7 +102,7 @@
|
|||
|
||||
<div class="mt-6 bg-base-300 p-4 rounded-lg">
|
||||
<p class="text-sm opacity-80">
|
||||
<span class="font-semibold text-warning">提示:</span>
|
||||
<span class="font-semibold text-error">提示:</span>
|
||||
请谨慎操作FPGA固化和热启动功能,确保上传的位流文件无误,以避免设备损坏。
|
||||
</p>
|
||||
</div>
|
||||
|
@ -106,21 +112,122 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { isNull, isUndefined } from "lodash";
|
||||
import { ref } from "vue";
|
||||
import { RemoteUpdaterClient } from "@/APIClient";
|
||||
import { useDialogStore } from "@/stores/dialog";
|
||||
import { Common } from "@/Common";
|
||||
|
||||
const dialog = useDialogStore();
|
||||
|
||||
// 编辑状态
|
||||
const isEditMode = ref(false);
|
||||
|
||||
// 选择热切换的比特流
|
||||
const selectBitstream = ref("黄金位流");
|
||||
|
||||
// 存储上传文件的信息
|
||||
const goldBitstreamFile = ref<string>('');
|
||||
const appBitstream1File = ref<string>('');
|
||||
const appBitstream2File = ref<string>('');
|
||||
const appBitstream3File = ref<string>('');
|
||||
const goldBitstreamFile = ref<File>();
|
||||
const appBitstream1File = ref<File>();
|
||||
const appBitstream2File = ref<File>();
|
||||
const appBitstream3File = ref<File>();
|
||||
|
||||
// 远程升级相关参数
|
||||
const devAddr = ref("192.168.1.100");
|
||||
const devPort = 1234;
|
||||
const remoteUpdater = new RemoteUpdaterClient();
|
||||
|
||||
// 处理文件上传
|
||||
function handleFileChange(event: Event, fileRef: any) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
const file = target.files?.[0]; // 获取选中的第一个文件
|
||||
|
||||
if (file) {
|
||||
fileRef.value = file.name;
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isUndefined(fileRef)) {
|
||||
fileRef.value = file;
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedBitstreamNum(): number {
|
||||
if (selectBitstream.value == "黄金位流") {
|
||||
return 0;
|
||||
} else if (selectBitstream.value == "应用位流1") {
|
||||
return 1;
|
||||
} else if (selectBitstream.value == "应用位流2") {
|
||||
return 2;
|
||||
} else if (selectBitstream.value == "应用位流3") {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function uploadAndDownloadBitstreams(
|
||||
devAddr: string,
|
||||
goldBitstream?: File,
|
||||
appBitstream1?: File,
|
||||
appBitstream2?: File,
|
||||
appBitstream3?: File,
|
||||
) {
|
||||
let cnt = 0;
|
||||
if (isUndefined(goldBitstream)) cnt++;
|
||||
if (isUndefined(appBitstream1)) cnt++;
|
||||
if (isUndefined(appBitstream2)) cnt++;
|
||||
if (isUndefined(appBitstream3)) cnt++;
|
||||
if ((cnt = 0)) {
|
||||
dialog.error("未选择比特流");
|
||||
}
|
||||
|
||||
try {
|
||||
{
|
||||
const ret = await remoteUpdater.uploadBitstreams(
|
||||
devAddr,
|
||||
Common.toFileParameterOrNull(goldBitstream),
|
||||
Common.toFileParameterOrNull(appBitstream1),
|
||||
Common.toFileParameterOrNull(appBitstream2),
|
||||
Common.toFileParameterOrNull(appBitstream3),
|
||||
);
|
||||
if (ret) {
|
||||
dialog.warn("上传比特流出错");
|
||||
} else {
|
||||
dialog.info("上传比特流成功");
|
||||
}
|
||||
}
|
||||
{
|
||||
const ret = await remoteUpdater.downloadMultiBitstreams(
|
||||
devAddr,
|
||||
devPort,
|
||||
getSelectedBitstreamNum(),
|
||||
);
|
||||
if (ret != cnt) {
|
||||
dialog.warn("固化比特流出错");
|
||||
} else {
|
||||
dialog.info("固化比特流成功");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dialog.error("比特流上传错误");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function hotresetBitstream(devAddr: string, bitstreamNum: number) {
|
||||
try {
|
||||
const ret = await remoteUpdater.hotResetBitstream(
|
||||
devAddr,
|
||||
devPort,
|
||||
bitstreamNum,
|
||||
);
|
||||
if (ret) {
|
||||
dialog.info("切换比特流成功");
|
||||
} else {
|
||||
dialog.error("切换比特流失败");
|
||||
}
|
||||
} catch (e) {
|
||||
dialog.error("切换比特流失败");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<template> <div class="bg-base-200 min-h-screen">
|
||||
<template>
|
||||
<div class="bg-base-200 min-h-screen">
|
||||
<main class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content flex-col lg:flex-row-reverse gap-8 lg:gap-12 py-10 px-4">
|
||||
<!-- 图片容器 -->
|
||||
<div class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
|
||||
<img src="https://placehold.co/600x400" class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
|
||||
<div
|
||||
class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
|
||||
<img src="https://placehold.co/600x400"
|
||||
class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
|
||||
<!-- 这里使用relative定位,限制覆盖层只在图片容器内 -->
|
||||
<div class="absolute inset-0 bg-primary opacity-10 rounded-2xl pointer-events-none"></div>
|
||||
</div>
|
||||
|
@ -14,64 +17,72 @@
|
|||
Welcome to
|
||||
</span>
|
||||
<span class="text-base-content">FPGA Web Lab!</span>
|
||||
<span class="absolute bottom-0 left-0 w-0 h-1 bg-primary transition-all duration-500 ease-in-out group-hover:w-3/4"></span>
|
||||
<span
|
||||
class="absolute bottom-0 left-0 w-0 h-1 bg-primary transition-all duration-500 ease-in-out group-hover:w-3/4"></span>
|
||||
</h1>
|
||||
|
||||
<p class="py-6 text-lg opacity-80 leading-relaxed">
|
||||
Prototype and simulate electronic circuits in your browser with our modern, intuitive interface. Create, test, and share your FPGA designs seamlessly.
|
||||
Prototype and simulate electronic circuits in your browser with our
|
||||
modern, intuitive interface. Create, test, and share your FPGA
|
||||
designs seamlessly.
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-4 actions-container">
|
||||
<router-link to="/project" class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<router-link to="/project"
|
||||
class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
||||
</svg>
|
||||
进入工程界面
|
||||
</router-link>
|
||||
|
||||
<router-link to="/login" class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<router-link to="/login"
|
||||
class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
登录
|
||||
</router-link>
|
||||
|
||||
<router-link to="/user" class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<router-link to="/user"
|
||||
class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
用户中心
|
||||
</router-link>
|
||||
<router-link to="/test" class="btn btn-info text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<router-link to="/test"
|
||||
class="btn btn-info text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
||||
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
||||
<polyline points="7 3 7 8 15 8"></polyline>
|
||||
</svg>
|
||||
测试功能
|
||||
</router-link>
|
||||
<router-link to="/test/jtag" class="btn btn-warning text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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>
|
||||
<line x1="8" y1="21" x2="16" y2="21"></line>
|
||||
<line x1="12" y1="17" x2="12" y2="21"></line>
|
||||
</svg>
|
||||
JTAG测试
|
||||
</router-link>
|
||||
|
||||
<router-link to="/admin" class="btn btn-error text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z"></path>
|
||||
<router-link to="/admin"
|
||||
class="btn btn-error text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5z">
|
||||
</path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
管理控制台
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md">
|
||||
<div
|
||||
class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md">
|
||||
<p class="text-sm">
|
||||
<span class="font-semibold text-primary">提示:</span> 您可以在工程界面中创建、编辑和测试您的FPGA项目,使用我们简洁直观的界面轻松进行硬件设计。
|
||||
<span class="font-semibold text-primary">提示:</span>
|
||||
您可以在工程界面中创建、编辑和测试您的FPGA项目,使用我们简洁直观的界面轻松进行硬件设计。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue