feat: add remote update function

This commit is contained in:
SikongJueluo 2025-05-14 21:22:20 +08:00
parent 48ae3b5975
commit 00ce79fa7b
No known key found for this signature in database
6 changed files with 520 additions and 283 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 {

20
src/Common.ts Normal file
View File

@ -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
}
}
}

View File

@ -1,6 +1,15 @@
<template>
<div class="bg-base-200 min-h-screen p-6">
<h1 class="text-3xl font-bold mb-6">FPGA 设备管理</h1>
<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,69 +34,66 @@
<!-- 表格内容 -->
<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>
<option>应用位流3</option>
</select>
</td>
<!-- 黄金位流上传区 -->
<!-- 黄金位流上传区 -->
<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上传区 -->
<!-- 应用位流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上传区 -->
<!-- 应用位流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上传区 -->
<!-- 应用位流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>

View File

@ -1,77 +1,88 @@
<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>
<!-- 内容容器 -->
<!-- 内容容器 -->
<div class="content-container max-w-md lg:max-w-2xl transform transition-all duration-500 ease-in-out">
<h1 class="text-4xl md:text-5xl font-bold mb-3 relative group">
<span class="relative z-10 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
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">
<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">
<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>