Merge branch 'master' of ssh://git.swordlost.top:222/SikongJueluo/FPGA_WebLab

This commit is contained in:
alivender 2025-07-14 17:15:37 +08:00
commit 683d918d30
11 changed files with 4852 additions and 3588 deletions

2
components.d.ts vendored
View File

@ -13,6 +13,7 @@ declare module 'vue' {
BaseBoard: typeof import('./src/components/equipments/BaseBoard.vue')['default'] BaseBoard: typeof import('./src/components/equipments/BaseBoard.vue')['default']
BaseInputField: typeof import('./src/components/InputField/BaseInputField.vue')['default'] BaseInputField: typeof import('./src/components/InputField/BaseInputField.vue')['default']
Canvas: typeof import('./src/components/Canvas.vue')['default'] Canvas: typeof import('./src/components/Canvas.vue')['default']
ChannelConfig: typeof import('./src/components/LogicAnalyzer/ChannelConfig.vue')['default']
CollapsibleSection: typeof import('./src/components/CollapsibleSection.vue')['default'] CollapsibleSection: typeof import('./src/components/CollapsibleSection.vue')['default']
ComponentSelector: typeof import('./src/components/LabCanvas/ComponentSelector.vue')['default'] ComponentSelector: typeof import('./src/components/LabCanvas/ComponentSelector.vue')['default']
DDR: typeof import('./src/components/equipments/DDR.vue')['default'] DDR: typeof import('./src/components/equipments/DDR.vue')['default']
@ -67,6 +68,7 @@ declare module 'vue' {
TabsTrigger: typeof import('reka-ui')['TabsTrigger'] TabsTrigger: typeof import('reka-ui')['TabsTrigger']
ThemeControlButton: typeof import('./src/components/ThemeControlButton.vue')['default'] ThemeControlButton: typeof import('./src/components/ThemeControlButton.vue')['default']
ThemeControlToggle: typeof import('./src/components/ThemeControlToggle.vue')['default'] ThemeControlToggle: typeof import('./src/components/ThemeControlToggle.vue')['default']
TriggerSettings: typeof import('./src/components/LogicAnalyzer/TriggerSettings.vue')['default']
TutorialCarousel: typeof import('./src/components/TutorialCarousel.vue')['default'] TutorialCarousel: typeof import('./src/components/TutorialCarousel.vue')['default']
UploadCard: typeof import('./src/components/UploadCard.vue')['default'] UploadCard: typeof import('./src/components/UploadCard.vue')['default']
WaveformDisplay: typeof import('./src/components/Oscilloscope/WaveformDisplay.vue')['default'] WaveformDisplay: typeof import('./src/components/Oscilloscope/WaveformDisplay.vue')['default']

View File

@ -346,25 +346,28 @@ public class UDPServer
return retPack.Value; return retPack.Value;
} }
private void ReceiveHandler(byte[] data, IPEndPoint endPoint) private async Task ReceiveHandler(byte[] data, IPEndPoint endPoint, DateTime time)
{ {
// Handle RemoteEP // 异步锁保护 udpData
if (endPoint is null) await Task.Run(() =>
{ {
logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:"); lock (udpData)
logger.Debug($" Original Data : {BitConverter.ToString(data).Replace("-", " ")}"); {
return; // Handle RemoteEP
} if (endPoint is null)
{
logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:");
logger.Debug($" Original Data : {BitConverter.ToString(data).Replace("-", " ")}");
return;
}
// 异步处理数据包 var udpDataObj = RecordUDPData(data, endPoint, time, Convert.ToInt32(data[1]));
Task.Run(() => PrintData(udpDataObj);
{ }
var udpData = RecordUDPData(data, endPoint, Convert.ToInt32(data[1]));
PrintData(udpData);
}); });
} }
private UDPData RecordUDPData(byte[] bytes, IPEndPoint remoteEP, int taskID) private UDPData RecordUDPData(byte[] bytes, IPEndPoint remoteEP, DateTime time, int taskID)
{ {
var remoteAddress = remoteEP.Address.ToString(); var remoteAddress = remoteEP.Address.ToString();
var remotePort = remoteEP.Port; var remotePort = remoteEP.Port;
@ -374,7 +377,7 @@ public class UDPServer
Port = remotePort, Port = remotePort,
TaskID = taskID, TaskID = taskID,
Data = bytes, Data = bytes,
DateTime = DateTime.Now, DateTime = time,
HasRead = false, HasRead = false,
}; };
@ -482,7 +485,7 @@ public class UDPServer
try try
{ {
UdpReceiveResult result = await client.ReceiveAsync(); UdpReceiveResult result = await client.ReceiveAsync();
ReceiveHandler(result.Buffer, result.RemoteEndPoint); ReceiveHandler(result.Buffer, result.RemoteEndPoint, DateTime.Now);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -510,6 +513,7 @@ public class UDPServer
item.Close(); item.Close();
} }
this.isRunning = false; this.isRunning = false;
} }
} }

View File

@ -465,6 +465,90 @@ export class VideoStreamClient {
} }
return Promise.resolve<any>(null as any); return Promise.resolve<any>(null as any);
} }
/**
*
* @return
*/
initAutoFocus(): Promise<FileResponse | null> {
let url_ = this.baseUrl + "/api/VideoStream/InitAutoFocus";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processInitAutoFocus(_response);
});
}
protected processInitAutoFocus(response: Response): Promise<FileResponse | null> {
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>(null as any);
}
/**
*
* @return
*/
autoFocus(): Promise<FileResponse | null> {
let url_ = this.baseUrl + "/api/VideoStream/AutoFocus";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/octet-stream"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processAutoFocus(_response);
});
}
protected processAutoFocus(response: Response): Promise<FileResponse | null> {
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>(null as any);
}
} }
export class BsdlParserClient { export class BsdlParserClient {
@ -1885,6 +1969,451 @@ export class JtagClient {
} }
} }
export class LogicAnalyzerClient {
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 captureOn (optional)
* @param force (optional)
* @return
*/
setCaptureMode(captureOn: boolean | undefined, force: boolean | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/SetCaptureMode?";
if (captureOn === null)
throw new Error("The parameter 'captureOn' cannot be null.");
else if (captureOn !== undefined)
url_ += "captureOn=" + encodeURIComponent("" + captureOn) + "&";
if (force === null)
throw new Error("The parameter 'force' cannot be null.");
else if (force !== undefined)
url_ += "force=" + encodeURIComponent("" + force) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processSetCaptureMode(_response);
});
}
protected processSetCaptureMode(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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} 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);
}
/**
*
* @return
*/
getCaptureStatus(): Promise<CaptureStatus> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/GetCaptureStatus";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processGetCaptureStatus(_response);
});
}
protected processGetCaptureStatus(response: Response): Promise<CaptureStatus> {
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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<CaptureStatus>(null as any);
}
/**
*
* @param mode (optional)
* @return
*/
setGlobalTrigMode(mode: GlobalCaptureMode | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/SetGlobalTrigMode?";
if (mode === null)
throw new Error("The parameter 'mode' cannot be null.");
else if (mode !== undefined)
url_ += "mode=" + encodeURIComponent("" + mode) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processSetGlobalTrigMode(_response);
});
}
protected processSetGlobalTrigMode(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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} 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);
}
/**
*
* @param signalIndex (optional) (0-7)
* @param op (optional)
* @param val (optional)
* @return
*/
setSignalTrigMode(signalIndex: number | undefined, op: SignalOperator | undefined, val: SignalValue | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/SetSignalTrigMode?";
if (signalIndex === null)
throw new Error("The parameter 'signalIndex' cannot be null.");
else if (signalIndex !== undefined)
url_ += "signalIndex=" + encodeURIComponent("" + signalIndex) + "&";
if (op === null)
throw new Error("The parameter 'op' cannot be null.");
else if (op !== undefined)
url_ += "op=" + encodeURIComponent("" + op) + "&";
if (val === null)
throw new Error("The parameter 'val' cannot be null.");
else if (val !== undefined)
url_ += "val=" + encodeURIComponent("" + val) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processSetSignalTrigMode(_response);
});
}
protected processSetSignalTrigMode(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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} 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);
}
/**
*
* @param config
* @return
*/
configureCapture(config: CaptureConfig): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/ConfigureCapture";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(config);
let options_: RequestInit = {
body: content_,
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processConfigureCapture(_response);
});
}
protected processConfigureCapture(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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} 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);
}
/**
*
* @return
*/
forceCapture(): Promise<boolean> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/ForceCapture";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processForceCapture(_response);
});
}
protected processForceCapture(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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} 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);
}
/**
*
* @return Base64编码
*/
getCaptureData(): Promise<string> {
let url_ = this.baseUrl + "/api/LogicAnalyzer/GetCaptureData";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processGetCaptureData(_response);
});
}
protected processGetCaptureData(response: Response): Promise<string> {
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 = 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 === 401) {
return response.text().then((_responseText) => {
let result401: any = null;
let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result401 = ProblemDetails.fromJS(resultData401);
return throwException("A server side error occurred.", status, _responseText, _headers, result401);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<string>(null as any);
}
}
export class MatrixKeyClient { export class MatrixKeyClient {
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }; private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
private baseUrl: string; private baseUrl: string;
@ -3286,6 +3815,154 @@ export interface IArgumentException extends ISystemException {
paramName?: string | undefined; paramName?: string | undefined;
} }
/** 逻辑分析仪运行状态枚举 */
export enum CaptureStatus {
None = 0,
CaptureOn = 1,
CaptureForce = 256,
CaptureBusy = 65536,
CaptureDone = 16777216,
}
/** 全局触发模式枚举,定义多路信号触发条件的逻辑组合方式 */
export enum GlobalCaptureMode {
AND = 0,
OR = 1,
NAND = 2,
NOR = 3,
}
/** 信号M的操作符枚举 */
export enum SignalOperator {
Equal = 0,
NotEqual = 1,
LessThan = 2,
LessThanOrEqual = 3,
GreaterThan = 4,
GreaterThanOrEqual = 5,
}
/** 信号M的值枚举 */
export enum SignalValue {
Logic0 = 0,
Logic1 = 1,
NotCare = 2,
Rise = 3,
Fall = 4,
RiseOrFall = 5,
NoChange = 6,
SomeNumber = 7,
}
/** 捕获配置 */
export class CaptureConfig implements ICaptureConfig {
/** 全局触发模式 */
globalMode!: GlobalCaptureMode;
/** 信号触发配置列表 */
signalConfigs!: SignalTriggerConfig[];
constructor(data?: ICaptureConfig) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
if (!data) {
this.signalConfigs = [];
}
}
init(_data?: any) {
if (_data) {
this.globalMode = _data["globalMode"];
if (Array.isArray(_data["signalConfigs"])) {
this.signalConfigs = [] as any;
for (let item of _data["signalConfigs"])
this.signalConfigs!.push(SignalTriggerConfig.fromJS(item));
}
}
}
static fromJS(data: any): CaptureConfig {
data = typeof data === 'object' ? data : {};
let result = new CaptureConfig();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["globalMode"] = this.globalMode;
if (Array.isArray(this.signalConfigs)) {
data["signalConfigs"] = [];
for (let item of this.signalConfigs)
data["signalConfigs"].push(item.toJSON());
}
return data;
}
}
/** 捕获配置 */
export interface ICaptureConfig {
/** 全局触发模式 */
globalMode: GlobalCaptureMode;
/** 信号触发配置列表 */
signalConfigs: SignalTriggerConfig[];
}
/** 信号触发配置 */
export class SignalTriggerConfig implements ISignalTriggerConfig {
/** 信号索引 (0-7) */
signalIndex!: number;
/** 操作符 */
operator!: SignalOperator;
/** 信号值 */
value!: SignalValue;
constructor(data?: ISignalTriggerConfig) {
if (data) {
for (var property in data) {
if (data.hasOwnProperty(property))
(<any>this)[property] = (<any>data)[property];
}
}
}
init(_data?: any) {
if (_data) {
this.signalIndex = _data["signalIndex"];
this.operator = _data["operator"];
this.value = _data["value"];
}
}
static fromJS(data: any): SignalTriggerConfig {
data = typeof data === 'object' ? data : {};
let result = new SignalTriggerConfig();
result.init(data);
return result;
}
toJSON(data?: any) {
data = typeof data === 'object' ? data : {};
data["signalIndex"] = this.signalIndex;
data["operator"] = this.operator;
data["value"] = this.value;
return data;
}
}
/** 信号触发配置 */
export interface ISignalTriggerConfig {
/** 信号索引 (0-7) */
signalIndex: number;
/** 操作符 */
operator: SignalOperator;
/** 信号值 */
value: SignalValue;
}
/** Package options which to send address to read or write */ /** Package options which to send address to read or write */
export class SendAddrPackOptions implements ISendAddrPackOptions { export class SendAddrPackOptions implements ISendAddrPackOptions {
/** 突发类型 */ /** 突发类型 */

View File

@ -0,0 +1,174 @@
<template>
<div class="space-y-4">
<!-- 通道状态概览 -->
<div class="stats stats-horizontal bg-base-100 shadow flex justify-between">
<div class="stat">
<div class="stat-title">总通道数</div>
<div class="stat-value text-primary">8</div>
<div class="stat-desc">逻辑分析仪通道</div>
</div>
<div class="stat">
<div class="stat-title">启用通道</div>
<div class="stat-value text-success">{{ enabledChannelCount }}</div>
<div class="stat-desc">当前激活通道</div>
</div>
<div class="stat">
<div class="stat-title">采样率</div>
<div class="stat-value text-info">100MHz</div>
<div class="stat-desc">最大采样频率</div>
</div>
</div>
<!-- 通道配置列表 -->
<div class="space-y-2">
<div class="flex items-center justify-between p-2 bg-base-300 rounded-lg">
<span class="font-medium">通道</span>
<span class="font-medium">启用</span>
<span class="font-medium">标签</span>
<span class="font-medium">颜色</span>
</div>
<div
v-for="(channel, index) in channels"
:key="index"
class="flex items-center justify-between p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
>
<!-- 通道编号 -->
<div class="flex items-center gap-2">
<span class="font-mono font-medium w-12">CH{{ index }}</span>
<div
class="w-3 h-3 rounded-full border-2 border-white shadow-sm"
:style="{ backgroundColor: channel.color }"
></div>
</div>
<!-- 启用开关 -->
<div class="form-control">
<input
type="checkbox"
v-model="channel.enabled"
class="toggle toggle-sm toggle-primary"
@change="updateChannelStatus(index)"
/>
</div>
<!-- 通道标签 -->
<div class="form-control w-32">
<input
type="text"
v-model="channel.label"
:placeholder="`通道 ${index}`"
class="input input-sm input-bordered w-full"
:disabled="!channel.enabled"
/>
</div>
<!-- 颜色选择 -->
<div class="form-control">
<input
type="color"
v-model="channel.color"
class="w-8 h-8 rounded border-2 border-base-300 cursor-pointer"
:disabled="!channel.enabled"
/>
</div>
</div>
</div>
<!-- 批量操作 -->
<div class="flex gap-2 pt-4">
<button @click="enableAllChannels" class="btn btn-primary btn-sm">
全部启用
</button>
<button @click="disableAllChannels" class="btn btn-outline btn-sm">
全部禁用
</button>
<button @click="resetChannelLabels" class="btn btn-outline btn-sm">
重置标签
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, reactive } from "vue";
//
interface Channel {
enabled: boolean;
label: string;
color: string;
}
//
interface Preset {
name: string;
description: string;
channels: Partial<Channel>[];
}
//
const defaultColors = [
"#FF5733",
"#33FF57",
"#3357FF",
"#FF33F5",
"#F5FF33",
"#33FFF5",
"#FF8C33",
"#8C33FF",
];
//
const channels = reactive<Channel[]>(
Array.from({ length: 8 }, (_, index) => ({
enabled: false,
label: `CH${index}`,
color: defaultColors[index],
})),
);
//
const enabledChannelCount = computed(
() => channels.filter((channel) => channel.enabled).length,
);
//
const updateChannelStatus = (index: number) => {
console.log(`通道 ${index} 状态更新:`, channels[index].enabled);
};
//
const enableAllChannels = () => {
channels.forEach((channel) => {
channel.enabled = true;
});
};
//
const disableAllChannels = () => {
channels.forEach((channel) => {
channel.enabled = false;
});
};
//
const resetChannelLabels = () => {
channels.forEach((channel, index) => {
channel.label = `CH${index}`;
});
};
//
const applyPreset = (preset: Preset) => {
preset.channels.forEach((presetChannel, index) => {
if (index < channels.length && presetChannel) {
Object.assign(channels[index], presetChannel);
}
});
};
</script>

View File

@ -1,6 +1,12 @@
<template> <template>
<div class="w-full h-100"> <div class="w-full h-150">
<v-chart v-if="true" class="w-full h-full" :option="option" autoresize /> <v-chart
v-if="data"
class="w-full h-full"
:option="option"
autoresize
:update-options="updateOptions"
/>
<div <div
v-else v-else
class="w-full h-full flex items-center justify-center text-gray-500" class="w-full h-full flex items-center justify-center text-gray-500"
@ -11,144 +17,194 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue"; import { computed, shallowRef } from "vue";
import VChart from "vue-echarts";
import type { LogicDataType } from "./index";
// Echarts // Echarts
import { use } from "echarts/core"; import { use } from "echarts/core";
import { LineChart } from "echarts/charts"; import { LineChart } from "echarts/charts";
import { import {
TitleComponent,
TooltipComponent, TooltipComponent,
LegendComponent,
ToolboxComponent,
GridComponent, GridComponent,
DataZoomComponent,
AxisPointerComponent,
ToolboxComponent,
} from "echarts/components"; } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import type { ComposeOption } from "echarts/core"; import type { ComposeOption } from "echarts/core";
import type { LineSeriesOption } from "echarts/charts"; import type { LineSeriesOption } from "echarts/charts";
import type { import type {
TitleComponentOption, AxisPointerComponentOption,
TooltipComponentOption, TooltipComponentOption,
LegendComponentOption,
ToolboxComponentOption,
GridComponentOption, GridComponentOption,
DataZoomComponentOption,
} from "echarts/components"; } from "echarts/components";
import type {
ToolboxComponentOption,
XAXisOption,
YAXisOption,
} from "echarts/types/dist/shared";
use([ use([
TitleComponent,
TooltipComponent, TooltipComponent,
LegendComponent,
ToolboxComponent, ToolboxComponent,
GridComponent, GridComponent,
AxisPointerComponent,
DataZoomComponent,
LineChart, LineChart,
CanvasRenderer, CanvasRenderer,
]); ]);
type EChartsOption = ComposeOption< type EChartsOption = ComposeOption<
| TitleComponentOption | AxisPointerComponentOption
| TooltipComponentOption | TooltipComponentOption
| LegendComponentOption
| ToolboxComponentOption | ToolboxComponentOption
| GridComponentOption | GridComponentOption
| DataZoomComponentOption
| LineSeriesOption | LineSeriesOption
>; >;
// Define props
interface Props {
data?: LogicDataType;
}
const props = defineProps<Props>();
//
const updateOptions = shallowRef({
notMerge: false,
lazyUpdate: true,
silent: false
});
const option = computed((): EChartsOption => { const option = computed((): EChartsOption => {
return { if (!props.data) return {};
title: {
text: "Stacked Area Chart", const channelCount = props.data.y.length;
const channelSpacing = 2; //
// 使
const grids: GridComponentOption[] = [
{
left: "5%",
right: "5%",
top: "5%",
bottom: "15%",
}, },
];
// X
const xAxis: XAXisOption[] = [
{
type: "category",
boundaryGap: false,
data: props.data!.x.map((x) => x.toFixed(3)),
axisLabel: {
formatter: (value: string) => `${value}${props.data!.xUnit}`,
},
},
];
// Y
const yAxis: YAXisOption[] = [
{
type: "value",
min: -0.5,
max: channelCount * channelSpacing - 0.5,
interval: channelSpacing,
axisLabel: {
formatter: (value: number) => {
const channelIndex = Math.round(value / channelSpacing);
return channelIndex < channelCount
? props.data!.channelNames[channelIndex]
: "";
},
},
splitLine: { show: false },
},
];
// Y
const series: LineSeriesOption[] = props.data.y.map((channelData, index) => ({
name: props.data!.channelNames[index],
type: "line",
data: channelData.map((value) => value + index * channelSpacing + 0.2),
step: "end",
lineStyle: {
width: 2,
},
areaStyle: {
opacity: 0.3,
origin: index * channelSpacing,
},
symbol: "none",
//
sampling: "lttb",
// large: true,
// largeThreshold: 2000,
// progressive: 2000,
//
animation: false,
}));
return {
//
animation: false,
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
axisPointer: { axisPointer: {
type: "cross", type: "line",
label: { label: {
backgroundColor: "#6a7985", backgroundColor: "#6a7985",
}, },
// axisPointer
animation: false,
}, },
}, formatter: (params: any) => {
legend: { if (Array.isArray(params) && params.length > 0) {
data: ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"], const timeValue = props.data!.x[params[0].dataIndex];
const dataIndex = params[0].dataIndex;
let tooltip = `Time: ${timeValue.toFixed(3)}${props.data!.xUnit}<br/>`;
// 01
props.data!.channelNames.forEach((channelName, index) => {
const originalValue = props.data!.y[index][dataIndex];
tooltip += `${channelName}: ${originalValue}<br/>`;
});
return tooltip;
}
return "";
},
// tooltip
hideDelay: 100,
}, },
toolbox: { toolbox: {
feature: { feature: {
saveAsImage: {}, restore: {},
}, },
}, },
grid: { grid: grids,
left: "3%", xAxis: xAxis,
right: "4%", yAxis: yAxis,
bottom: "3%", dataZoom: [
containLabel: true,
},
xAxis: [
{ {
type: "category", show: true,
boundaryGap: false, realtime: true,
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], start: 0,
}, end: 100,
], },
yAxis: [ {
{ type: "inside",
type: "value", realtime: true,
}, start: 0,
], end: 100,
series: [
{
name: "Email",
type: "line",
stack: "Total",
areaStyle: {},
emphasis: {
focus: "series",
},
data: [120, 132, 101, 134, 90, 230, 210],
},
{
name: "Union Ads",
type: "line",
stack: "Total",
areaStyle: {},
emphasis: {
focus: "series",
},
data: [220, 182, 191, 234, 290, 330, 310],
},
{
name: "Video Ads",
type: "line",
stack: "Total",
areaStyle: {},
emphasis: {
focus: "series",
},
data: [150, 232, 201, 154, 190, 330, 410],
},
{
name: "Direct",
type: "line",
stack: "Total",
areaStyle: {},
emphasis: {
focus: "series",
},
data: [320, 332, 301, 334, 390, 330, 320],
},
{
name: "Search Engine",
type: "line",
stack: "Total",
label: {
show: true,
position: "top",
},
areaStyle: {},
emphasis: {
focus: "series",
},
data: [820, 932, 901, 934, 1290, 1330, 1320],
}, },
], ],
series: series,
}; };
}); });
</script> </script>

View File

@ -0,0 +1,266 @@
<template>
<div class="space-y-4">
<!-- 全局触发模式配置 -->
<div class="form-control">
<label class="label">
<span class="label-text font-medium">全局触发模式</span>
</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<button
v-for="mode in globalModes"
:key="mode.value"
@click="setGlobalMode(mode.value)"
:class="[
'btn btn-sm',
currentGlobalMode === mode.value ? 'btn-primary' : 'btn-outline',
]"
>
{{ mode.label }}
</button>
</div>
<div class="label">
<span class="label-text-alt text-gray-500">
选择多路信号触发条件的逻辑组合方式
</span>
</div>
</div>
<!-- 信号触发配置 -->
<div class="form-control">
<label class="label">
<span class="label-text font-medium">信号触发配置</span>
</label>
<div class="space-y-2">
<div
v-for="(signal, index) in signalConfigs"
:key="index"
class="flex items-center gap-2 p-3 bg-base-200 rounded-lg"
>
<span class="text-sm font-medium w-16">CH{{ index }}</span>
<!-- 操作符选择 -->
<select
v-model="signal.operator"
class="select select-sm select-bordered w-32"
>
<option v-for="op in operators" :key="op.value" :value="op.value">
{{ op.label }}
</option>
</select>
<!-- 信号值选择 -->
<select
v-model="signal.value"
class="select select-sm select-bordered w-32"
>
<option
v-for="val in signalValues"
:key="val.value"
:value="val.value"
>
{{ val.label }}
</option>
</select>
<!-- 启用/禁用开关 -->
<div class="form-control">
<label class="label cursor-pointer gap-2">
<span class="label-text text-sm">启用</span>
<input
type="checkbox"
v-model="signal.enabled"
class="toggle toggle-sm toggle-primary"
/>
</label>
</div>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="flex gap-2 pt-4">
<button
@click="applyConfiguration"
:disabled="isApplying"
class="btn btn-primary btn-sm"
>
<span
v-if="isApplying"
class="loading loading-spinner loading-sm"
></span>
应用配置
</button>
<button @click="resetConfiguration" class="btn btn-outline btn-sm">
重置
</button>
</div>
<!-- 状态指示 -->
<div
v-if="lastApplyResult"
class="alert alert-sm"
:class="lastApplyResult.success ? 'alert-success' : 'alert-error'"
>
<span>{{ lastApplyResult.message }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from "vue";
import { useEquipments } from "@/stores/equipments";
import {
CaptureConfig,
LogicAnalyzerClient,
GlobalCaptureMode,
SignalOperator,
SignalValue,
type SignalTriggerConfig,
} from "@/APIClient";
import { AuthManager } from "@/utils/AuthManager";
// 使
const equipments = useEquipments();
//
const currentGlobalMode = ref<GlobalCaptureMode>(GlobalCaptureMode.AND);
//
const isApplying = ref(false);
const lastApplyResult = ref<{ success: boolean; message: string } | null>(null);
//
const globalModes = [
{
value: GlobalCaptureMode.AND,
label: "AND",
description: "所有条件都满足时触发",
},
{
value: GlobalCaptureMode.OR,
label: "OR",
description: "任一条件满足时触发",
},
{ value: GlobalCaptureMode.NAND, label: "NAND", description: "AND的非" },
{ value: GlobalCaptureMode.NOR, label: "NOR", description: "OR的非" },
];
//
const operators = [
{ value: SignalOperator.Equal, label: "=" },
{ value: SignalOperator.NotEqual, label: "≠" },
{ value: SignalOperator.LessThan, label: "<" },
{ value: SignalOperator.LessThanOrEqual, label: "≤" },
{ value: SignalOperator.GreaterThan, label: ">" },
{ value: SignalOperator.GreaterThanOrEqual, label: "≥" },
];
//
const signalValues = [
{ value: SignalValue.Logic0, label: "0" },
{ value: SignalValue.Logic1, label: "1" },
{ value: SignalValue.NotCare, label: "X" },
{ value: SignalValue.Rise, label: "↑" },
{ value: SignalValue.Fall, label: "↓" },
{ value: SignalValue.RiseOrFall, label: "↕" },
{ value: SignalValue.NoChange, label: "—" },
{ value: SignalValue.SomeNumber, label: "#" },
];
// 8
const signalConfigs = reactive(
Array.from({ length: 8 }, (_, index) => ({
signalIndex: index,
operator: SignalOperator.Equal,
value: SignalValue.Logic1,
enabled: false,
})),
);
//
const setGlobalMode = async (mode: GlobalCaptureMode) => {
try {
const client = AuthManager.createAuthenticatedLogicAnalyzerClient();
const success = await client.setGlobalTrigMode(mode);
if (success) {
currentGlobalMode.value = mode;
lastApplyResult.value = {
success: true,
message: `全局触发模式已设置为 ${globalModes.find((m) => m.value === mode)?.label}`,
};
} else {
throw new Error("设置失败");
}
} catch (error) {
console.error("设置全局触发模式失败:", error);
lastApplyResult.value = {
success: false,
message: "设置全局触发模式失败",
};
}
};
//
const applyConfiguration = async () => {
isApplying.value = true;
lastApplyResult.value = null;
try {
const client = AuthManager.createAuthenticatedLogicAnalyzerClient();
//
const enabledSignals = signalConfigs.filter((signal) => signal.enabled);
const config = new CaptureConfig({
globalMode: currentGlobalMode.value,
signalConfigs: enabledSignals.map(
(signal) =>
({
signalIndex: signal.signalIndex,
operator: signal.operator,
value: signal.value,
}) as SignalTriggerConfig,
),
});
//
const success = await client.configureCapture(config);
if (success) {
lastApplyResult.value = {
success: true,
message: `配置已成功应用,启用了 ${enabledSignals.length} 个通道`,
};
} else {
throw new Error("应用配置失败");
}
} catch (error) {
console.error("应用配置失败:", error);
lastApplyResult.value = {
success: false,
message: "应用配置失败,请检查设备连接",
};
} finally {
isApplying.value = false;
}
};
//
const resetConfiguration = () => {
currentGlobalMode.value = GlobalCaptureMode.AND;
signalConfigs.forEach((signal) => {
signal.operator = SignalOperator.Equal;
signal.value = SignalValue.Logic1;
signal.enabled = false;
});
lastApplyResult.value = null;
};
//
setTimeout(() => {
if (lastApplyResult.value) {
lastApplyResult.value = null;
}
}, 5000);
</script>

View File

@ -0,0 +1,78 @@
import LogicalWaveFormDisplay from "./LogicalWaveFormDisplay.vue";
type LogicDataType = {
x: number[];
y: number[][]; // 8 channels of digital data (0 or 1)
xUnit: "s" | "ms" | "us" | "ns";
channelNames: string[];
};
// Test data generator for 8-channel digital signals
function generateTestLogicData(): LogicDataType {
const sampleRate = 10000; // 10kHz sampling
const duration = 1;
const points = Math.floor(sampleRate * duration);
const x = Array.from({ length: points }, (_, i) => (i / sampleRate) * 1000); // time in ms
// Generate 8 channels with different digital patterns
const y = [
// Channel 0: Clock signal 100Hz
Array.from(
{ length: points },
(_, i) => Math.floor((100 * i) / sampleRate) % 2,
),
// Channel 1: Clock/2 signal 50Hz
Array.from(
{ length: points },
(_, i) => Math.floor((50 * i) / sampleRate) % 2,
),
// Channel 2: Clock/4 signal 25Hz
Array.from(
{ length: points },
(_, i) => Math.floor((25 * i) / sampleRate) % 2,
),
// Channel 3: Clock/8 signal 12.5Hz
Array.from(
{ length: points },
(_, i) => Math.floor((12.5 * i) / sampleRate) % 2,
),
// Channel 4: Data signal (pseudo-random pattern)
Array.from(
{ length: points },
(_, i) => Math.abs( Math.floor(Math.sin(i * 0.01) * 10) % 2 ),
),
// Channel 5: Enable signal (periodic pulse)
Array.from(
{ length: points },
(_, i) => (Math.floor(i / 50) % 10) < 3 ? 1 : 0,
),
// Channel 6: Reset signal (occasional pulse)
Array.from(
{ length: points },
(_, i) => (Math.floor(i / 200) % 20) === 0 ? 1 : 0,
),
// Channel 7: Status signal (slow changing)
Array.from(
{ length: points },
(_, i) => Math.floor(i / 1000) % 2,
),
];
const channelNames = [
"CLK",
"CLK/2",
"CLK/4",
"CLK/8",
"PWM",
"ENABLE",
"RESET",
"STATUS"
];
return { x, y, xUnit: "ms", channelNames };
}
export { LogicalWaveFormDisplay, generateTestLogicData, type LogicDataType };
export { default as TriggerSettings } from './TriggerSettings.vue'
export { default as ChannelConfig } from './ChannelConfig.vue'

View File

@ -31,7 +31,6 @@ import { CanvasRenderer } from "echarts/renderers";
import type { ComposeOption } from "echarts/core"; import type { ComposeOption } from "echarts/core";
import type { LineSeriesOption } from "echarts/charts"; import type { LineSeriesOption } from "echarts/charts";
import type { import type {
TitleComponentOption,
TooltipComponentOption, TooltipComponentOption,
LegendComponentOption, LegendComponentOption,
ToolboxComponentOption, ToolboxComponentOption,
@ -40,7 +39,6 @@ import type {
} from "echarts/components"; } from "echarts/components";
use([ use([
TitleComponent,
TooltipComponent, TooltipComponent,
LegendComponent, LegendComponent,
ToolboxComponent, ToolboxComponent,
@ -51,7 +49,6 @@ use([
]); ]);
type EChartsOption = ComposeOption< type EChartsOption = ComposeOption<
| TitleComponentOption
| TooltipComponentOption | TooltipComponentOption
| LegendComponentOption | LegendComponentOption
| ToolboxComponentOption | ToolboxComponentOption

View File

@ -9,6 +9,7 @@ import {
RemoteUpdateClient, RemoteUpdateClient,
TutorialClient, TutorialClient,
UDPClient, UDPClient,
LogicAnalyzerClient,
} from "@/APIClient"; } from "@/APIClient";
// 支持的客户端类型联合类型 // 支持的客户端类型联合类型
@ -22,6 +23,7 @@ type SupportedClient =
| PowerClient | PowerClient
| RemoteUpdateClient | RemoteUpdateClient
| TutorialClient | TutorialClient
| LogicAnalyzerClient
| UDPClient; | UDPClient;
export class AuthManager { export class AuthManager {
@ -151,6 +153,10 @@ export class AuthManager {
return AuthManager.createAuthenticatedClient(UDPClient); return AuthManager.createAuthenticatedClient(UDPClient);
} }
public static createAuthenticatedLogicAnalyzerClient(): LogicAnalyzerClient {
return AuthManager.createAuthenticatedClient(LogicAnalyzerClient);
}
// 登录函数 // 登录函数
public static async login( public static async login(
username: string, username: string,
@ -212,11 +218,7 @@ export class AuthManager {
} catch (error) { } catch (error) {
// 只有在token完全无效的情况下才清除token // 只有在token完全无效的情况下才清除token
// 401错误表示token有效但权限不足不应清除token // 401错误表示token有效但权限不足不应清除token
if ( if (error && typeof error === "object" && "status" in error) {
error &&
typeof error === "object" &&
"status" in error
) {
// 如果是403 (Forbidden) 或401 (Unauthorized)说明token有效但权限不足 // 如果是403 (Forbidden) 或401 (Unauthorized)说明token有效但权限不足
if (error.status === 401 || error.status === 403) { if (error.status === 401 || error.status === 403) {
return false; return false;
@ -225,7 +227,7 @@ export class AuthManager {
AuthManager.clearToken(); AuthManager.clearToken();
} else { } else {
// 网络错误等不清除token // 网络错误等不清除token
console.error('管理员权限验证失败:', error); console.error("管理员权限验证失败:", error);
} }
return false; return false;
} }

View File

@ -6,7 +6,7 @@
type="radio" type="radio"
name="function-bar" name="function-bar"
id="1" id="1"
checked :checked="checkID === 1"
@change="handleTabChange" @change="handleTabChange"
/> />
<TerminalIcon class="icon" /> <TerminalIcon class="icon" />
@ -17,6 +17,7 @@
type="radio" type="radio"
name="function-bar" name="function-bar"
id="2" id="2"
:checked="checkID === 2"
@change="handleTabChange" @change="handleTabChange"
/> />
<VideoIcon class="icon" /> <VideoIcon class="icon" />
@ -27,6 +28,7 @@
type="radio" type="radio"
name="function-bar" name="function-bar"
id="3" id="3"
:checked="checkID === 3"
@change="handleTabChange" @change="handleTabChange"
/> />
<SquareActivityIcon class="icon" /> <SquareActivityIcon class="icon" />
@ -37,6 +39,7 @@
type="radio" type="radio"
name="function-bar" name="function-bar"
id="4" id="4"
:checked="checkID === 4"
@change="handleTabChange" @change="handleTabChange"
/> />
<Zap class="icon" /> <Zap class="icon" />

View File

@ -7,7 +7,7 @@
<Zap class="w-5 h-5" /> <Zap class="w-5 h-5" />
逻辑信号分析 逻辑信号分析
</h2> </h2>
<LogicalWaveFormDisplay /> <LogicalWaveFormDisplay :data="generateTestLogicData()" />
</div> </div>
</div> </div>
@ -38,7 +38,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { Zap, Settings, Layers } from "lucide-vue-next"; import { Zap, Settings, Layers } from "lucide-vue-next";
import { useEquipments } from "@/stores/equipments"; import { useEquipments } from "@/stores/equipments";
import LogicalWaveFormDisplay from "@/components/LogicAnalyzer/LogicalWaveFormDisplay.vue"; import {
LogicalWaveFormDisplay,
generateTestLogicData,
TriggerSettings,
ChannelConfig
} from "@/components/LogicAnalyzer";
// 使 // 使
const equipments = useEquipments(); const equipments = useEquipments();