feat: 持续完善逻辑分析仪的界面
This commit is contained in:
174
src/components/LogicAnalyzer/ChannelConfig.vue
Normal file
174
src/components/LogicAnalyzer/ChannelConfig.vue
Normal 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>
|
||||
266
src/components/LogicAnalyzer/TriggerSettings.vue
Normal file
266
src/components/LogicAnalyzer/TriggerSettings.vue
Normal 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>
|
||||
@@ -73,4 +73,6 @@ function generateTestLogicData(): LogicDataType {
|
||||
return { x, y, xUnit: "ms", channelNames };
|
||||
}
|
||||
|
||||
export { LogicalWaveFormDisplay, generateTestLogicData, type LogicDataType };
|
||||
export { LogicalWaveFormDisplay, generateTestLogicData, type LogicDataType };
|
||||
export { default as TriggerSettings } from './TriggerSettings.vue'
|
||||
export { default as ChannelConfig } from './ChannelConfig.vue'
|
||||
Reference in New Issue
Block a user