feat: 持续完善逻辑分析仪的界面

This commit is contained in:
SikongJueluo 2025-07-14 16:42:30 +08:00
parent 4d6c06a0e0
commit c9fc6961fa
No known key found for this signature in database
7 changed files with 4605 additions and 3477 deletions

2
components.d.ts vendored
View File

@ -13,6 +13,7 @@ declare module 'vue' {
BaseBoard: typeof import('./src/components/equipments/BaseBoard.vue')['default']
BaseInputField: typeof import('./src/components/InputField/BaseInputField.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']
ComponentSelector: typeof import('./src/components/LabCanvas/ComponentSelector.vue')['default']
DDR: typeof import('./src/components/equipments/DDR.vue')['default']
@ -67,6 +68,7 @@ declare module 'vue' {
TabsTrigger: typeof import('reka-ui')['TabsTrigger']
ThemeControlButton: typeof import('./src/components/ThemeControlButton.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']
UploadCard: typeof import('./src/components/UploadCard.vue')['default']
WaveformDisplay: typeof import('./src/components/Oscilloscope/WaveformDisplay.vue')['default']

View File

@ -465,6 +465,90 @@ export class VideoStreamClient {
}
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 {
@ -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 {
private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
private baseUrl: string;
@ -3286,6 +3815,154 @@ export interface IArgumentException extends ISystemException {
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 */
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

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

@ -74,3 +74,5 @@ function generateTestLogicData(): LogicDataType {
}
export { LogicalWaveFormDisplay, generateTestLogicData, type LogicDataType };
export { default as TriggerSettings } from './TriggerSettings.vue'
export { default as ChannelConfig } from './ChannelConfig.vue'

View File

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

View File

@ -38,7 +38,12 @@
<script setup lang="ts">
import { Zap, Settings, Layers } from "lucide-vue-next";
import { useEquipments } from "@/stores/equipments";
import { LogicalWaveFormDisplay, generateTestLogicData } from "@/components/LogicAnalyzer";
import {
LogicalWaveFormDisplay,
generateTestLogicData,
TriggerSettings,
ChannelConfig
} from "@/components/LogicAnalyzer";
// 使
const equipments = useEquipments();