Merge branch 'master' of ssh://git.swordlost.top:222/SikongJueluo/FPGA_WebLab into dpp
This commit is contained in:
commit
474151d412
|
@ -148,6 +148,9 @@ class Camera
|
|||
// var resetResult2 = await Reset();
|
||||
// if (!resetResult2.IsSuccessful) return resetResult2;
|
||||
|
||||
var autofocusResult = await InitAutoFocus();
|
||||
if (!autofocusResult.IsSuccessful) return autofocusResult;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1270,6 +1273,32 @@ class Camera
|
|||
var result = await ConfigureRegisters(focusRegisters);
|
||||
if (!result.IsSuccessful) return result;
|
||||
|
||||
// 读取寄存器判断初始化是否完毕
|
||||
for (int iteration = 1000; iteration > 0; iteration--)
|
||||
{
|
||||
var readResult = await ReadRegister(0x3029);
|
||||
if (!readResult.IsSuccessful)
|
||||
{
|
||||
logger.Error($"读取自动对焦初始化状态失败: {readResult.Error}");
|
||||
return new(readResult.Error);
|
||||
}
|
||||
|
||||
logger.Trace($"自动对焦初始化状态检查, state=0x{readResult.Value:X2}");
|
||||
|
||||
if (readResult.Value == 0x70)
|
||||
{
|
||||
break; // 初始化完成
|
||||
}
|
||||
|
||||
if (iteration == 1)
|
||||
{
|
||||
logger.Error($"自动对焦初始化状态检查超时!! state=0x{readResult.Value:X2}");
|
||||
return new(new Exception($"自动对焦初始化状态检查超时, state=0x{readResult.Value:X2}"));
|
||||
}
|
||||
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
logger.Info("OV5640自动对焦功能初始化完成");
|
||||
return true;
|
||||
}
|
||||
|
@ -1293,10 +1322,7 @@ class Camera
|
|||
logger.Info("已发送单点对焦命令 (0x3022 = 0x03)");
|
||||
|
||||
// 步骤2: 读取寄存器 0x3029 的状态,如果返回值为 0x10,代表对焦已完成
|
||||
int iteration = 5000;
|
||||
byte focusStatus;
|
||||
|
||||
do
|
||||
for (int iteration = 5000; iteration > 0; iteration--)
|
||||
{
|
||||
var readResult = await ReadRegisters(0x3029);
|
||||
if (!readResult.IsSuccessful)
|
||||
|
@ -1305,23 +1331,20 @@ class Camera
|
|||
return new(readResult.Error);
|
||||
}
|
||||
|
||||
focusStatus = readResult.Value;
|
||||
|
||||
if (focusStatus == 0x10)
|
||||
if (readResult.Value == 0x10)
|
||||
{
|
||||
logger.Info("对焦已完成 (0x3029 = 0x10)");
|
||||
break;
|
||||
}
|
||||
|
||||
if (iteration-- == 0)
|
||||
if (iteration == 1)
|
||||
{
|
||||
logger.Error($"自动对焦超时,状态: 0x{focusStatus:X2}");
|
||||
return new(new Exception($"自动对焦超时,状态: 0x{focusStatus:X2}"));
|
||||
logger.Error($"自动对焦超时,状态: 0x{readResult.Value:X2}");
|
||||
return new(new Exception($"自动对焦超时,状态: 0x{readResult.Value:X2}"));
|
||||
}
|
||||
|
||||
await Task.Delay(100);
|
||||
}
|
||||
while (focusStatus != 0x10);
|
||||
|
||||
// 步骤3: 写寄存器 0x3022 为 0x06,暂停对焦过程,使镜头将保持在此对焦位置
|
||||
var pauseResult = await ConfigureRegisters([[0x3022, 0x06]]);
|
||||
|
@ -1331,7 +1354,7 @@ class Camera
|
|||
return pauseResult;
|
||||
}
|
||||
|
||||
logger.Info($"自动对焦完成并暂停,镜头保持在对焦位置,剩余迭代次数: {iteration}");
|
||||
logger.Info("自动对焦完成并暂停,镜头保持在对焦位置");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
<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>
|
|
@ -0,0 +1,322 @@
|
|||
import { createInjectionState } from "@vueuse/core";
|
||||
import { shallowRef, reactive, ref, computed } from "vue";
|
||||
import {
|
||||
CaptureConfig,
|
||||
LogicAnalyzerClient,
|
||||
GlobalCaptureMode,
|
||||
SignalOperator,
|
||||
SignalValue,
|
||||
type SignalTriggerConfig,
|
||||
} from "@/APIClient";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
|
||||
export type LogicDataType = {
|
||||
x: number[];
|
||||
y: number[][]; // 8 channels of digital data (0 or 1)
|
||||
xUnit: "s" | "ms" | "us" | "ns";
|
||||
};
|
||||
|
||||
// 通道接口定义
|
||||
export type Channel = {
|
||||
enabled: boolean;
|
||||
label: string;
|
||||
color: string;
|
||||
};
|
||||
|
||||
// 全局模式选项
|
||||
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: "#" },
|
||||
];
|
||||
|
||||
// 默认颜色数组
|
||||
const defaultColors = [
|
||||
"#FF5733",
|
||||
"#33FF57",
|
||||
"#3357FF",
|
||||
"#FF33F5",
|
||||
"#F5FF33",
|
||||
"#33FFF5",
|
||||
"#FF8C33",
|
||||
"#8C33FF",
|
||||
];
|
||||
|
||||
const [useProvideLogicAnalyzer, useLogicAnalyzerState] = createInjectionState(
|
||||
() => {
|
||||
const logicData = shallowRef<LogicDataType>();
|
||||
const alert = useAlertStore();
|
||||
|
||||
// 触发设置相关状态
|
||||
const currentGlobalMode = ref<GlobalCaptureMode>(GlobalCaptureMode.AND);
|
||||
const isApplying = ref(false);
|
||||
|
||||
// 通道配置
|
||||
const channels = reactive<Channel[]>(
|
||||
Array.from({ length: 8 }, (_, index) => ({
|
||||
enabled: false,
|
||||
label: `CH${index}`,
|
||||
color: defaultColors[index],
|
||||
})),
|
||||
);
|
||||
|
||||
// 8个信号通道的配置
|
||||
const signalConfigs = reactive(
|
||||
Array.from({ length: 8 }, (_, index) => ({
|
||||
signalIndex: index,
|
||||
operator: SignalOperator.Equal,
|
||||
value: SignalValue.Logic1,
|
||||
})),
|
||||
);
|
||||
|
||||
// 计算启用的通道数量
|
||||
const enabledChannelCount = computed(
|
||||
() => channels.filter((channel) => channel.enabled).length,
|
||||
);
|
||||
|
||||
// 添加计算属性:获取通道名称数组
|
||||
const channelNames = computed(() =>
|
||||
channels.map(channel => channel.label)
|
||||
);
|
||||
|
||||
// 添加计算属性:获取启用通道的名称数组
|
||||
const enabledChannelNames = computed(() =>
|
||||
channels.filter(channel => channel.enabled).map(channel => channel.label)
|
||||
);
|
||||
|
||||
const enableAllChannels = () => {
|
||||
channels.forEach((channel) => {
|
||||
channel.enabled = true;
|
||||
});
|
||||
};
|
||||
|
||||
const disableAllChannels = () => {
|
||||
channels.forEach((channel) => {
|
||||
channel.enabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
const setGlobalMode = async (mode: GlobalCaptureMode) => {
|
||||
try {
|
||||
const client = AuthManager.createAuthenticatedLogicAnalyzerClient();
|
||||
const success = await client.setGlobalTrigMode(mode);
|
||||
|
||||
if (success) {
|
||||
currentGlobalMode.value = mode;
|
||||
alert?.success(
|
||||
`全局触发模式已设置为 ${globalModes.find((m) => m.value === mode)?.label}`,
|
||||
3000,
|
||||
);
|
||||
} else {
|
||||
throw new Error("设置失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("设置全局触发模式失败:", error);
|
||||
alert?.error("设置全局触发模式失败", 3000);
|
||||
}
|
||||
};
|
||||
|
||||
const applyConfiguration = async () => {
|
||||
isApplying.value = true;
|
||||
|
||||
try {
|
||||
const client = AuthManager.createAuthenticatedLogicAnalyzerClient();
|
||||
|
||||
// 准备配置数据 - 只包含启用的通道
|
||||
const enabledSignals = signalConfigs.filter(
|
||||
(signal, index) => channels[index].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) {
|
||||
const enabledChannelCount = channels.filter(
|
||||
(ch) => ch.enabled,
|
||||
).length;
|
||||
alert?.success(
|
||||
`配置已成功应用,启用了 ${enabledChannelCount} 个通道和触发条件`,
|
||||
3000,
|
||||
);
|
||||
} else {
|
||||
throw new Error("应用配置失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("应用配置失败:", error);
|
||||
alert?.error("应用配置失败,请检查设备连接", 3000);
|
||||
} finally {
|
||||
isApplying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetConfiguration = () => {
|
||||
currentGlobalMode.value = GlobalCaptureMode.AND;
|
||||
|
||||
channels.forEach((channel, index) => {
|
||||
channel.enabled = false;
|
||||
channel.label = `CH${index}`;
|
||||
channel.color = defaultColors[index];
|
||||
});
|
||||
|
||||
signalConfigs.forEach((signal) => {
|
||||
signal.operator = SignalOperator.Equal;
|
||||
signal.value = SignalValue.Logic1;
|
||||
});
|
||||
|
||||
alert?.info("配置已重置", 2000);
|
||||
};
|
||||
|
||||
// 添加设置逻辑数据的方法
|
||||
const setLogicData = (data: LogicDataType) => {
|
||||
logicData.value = data;
|
||||
};
|
||||
|
||||
// 添加生成测试数据的方法
|
||||
const generateTestData = () => {
|
||||
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 testChannelNames = [
|
||||
"CLK",
|
||||
"CLK/2",
|
||||
"CLK/4",
|
||||
"CLK/8",
|
||||
"PWM",
|
||||
"ENABLE",
|
||||
"RESET",
|
||||
"STATUS"
|
||||
];
|
||||
|
||||
channels.forEach((channel, index) => {
|
||||
channel.label = testChannelNames[index];
|
||||
});
|
||||
|
||||
// 设置逻辑数据
|
||||
setLogicData({ x, y, xUnit: "ms" });
|
||||
|
||||
alert?.success("测试数据生成成功", 2000);
|
||||
};
|
||||
|
||||
return {
|
||||
// 原有的逻辑数据
|
||||
logicData,
|
||||
|
||||
// 触发设置状态
|
||||
currentGlobalMode,
|
||||
isApplying,
|
||||
channels,
|
||||
signalConfigs,
|
||||
enabledChannelCount,
|
||||
channelNames,
|
||||
enabledChannelNames,
|
||||
|
||||
// 选项数据
|
||||
globalModes,
|
||||
operators,
|
||||
signalValues,
|
||||
|
||||
// 触发设置方法
|
||||
enableAllChannels,
|
||||
disableAllChannels,
|
||||
setGlobalMode,
|
||||
applyConfiguration,
|
||||
resetConfiguration,
|
||||
setLogicData,
|
||||
generateTestData,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export { useProvideLogicAnalyzer, useLogicAnalyzerState };
|
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<div class="w-full h-150">
|
||||
<div
|
||||
class="w-full"
|
||||
:class="{
|
||||
'h-48': !analyzer.logicData.value,
|
||||
'h-150': analyzer.logicData.value,
|
||||
}"
|
||||
>
|
||||
<v-chart
|
||||
v-if="data"
|
||||
v-if="analyzer.logicData.value"
|
||||
class="w-full h-full"
|
||||
:option="option"
|
||||
autoresize
|
||||
|
@ -9,9 +15,29 @@
|
|||
/>
|
||||
<div
|
||||
v-else
|
||||
class="w-full h-full flex items-center justify-center text-gray-500"
|
||||
class="w-full h-full flex flex-col gap-6 items-center justify-center"
|
||||
>
|
||||
暂无数据
|
||||
<div class="text-center">
|
||||
<h3 class="text-xl font-semibold text-slate-600 mb-2">
|
||||
暂无逻辑分析数据
|
||||
</h3>
|
||||
<p class="text-sm text-slate-500">点击下方按钮生成测试数据用于观察</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="group relative px-8 py-3 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-300 active:scale-95"
|
||||
@click="analyzer.generateTestData"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<RefreshCcw
|
||||
class="w-5 h-5 group-hover:rotate-180 transition-transform duration-300"
|
||||
/>
|
||||
生成测试数据
|
||||
</span>
|
||||
<div
|
||||
class="absolute inset-0 bg-white opacity-0 group-hover:opacity-20 rounded-lg transition-opacity duration-200"
|
||||
></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -19,7 +45,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, shallowRef } from "vue";
|
||||
import VChart from "vue-echarts";
|
||||
import type { LogicDataType } from "./index";
|
||||
import { RefreshCcw } from "lucide-vue-next";
|
||||
|
||||
// Echarts
|
||||
import { use } from "echarts/core";
|
||||
|
@ -45,6 +71,9 @@ import type {
|
|||
XAXisOption,
|
||||
YAXisOption,
|
||||
} from "echarts/types/dist/shared";
|
||||
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
|
||||
import { useRequiredInjection } from "@/utils/Common";
|
||||
import { isUndefined } from "lodash";
|
||||
|
||||
use([
|
||||
TooltipComponent,
|
||||
|
@ -65,24 +94,19 @@ type EChartsOption = ComposeOption<
|
|||
| LineSeriesOption
|
||||
>;
|
||||
|
||||
// Define props
|
||||
interface Props {
|
||||
data?: LogicDataType;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const analyzer = useRequiredInjection(useLogicAnalyzerState);
|
||||
|
||||
// 添加更新选项来减少重绘
|
||||
const updateOptions = shallowRef({
|
||||
notMerge: false,
|
||||
lazyUpdate: true,
|
||||
silent: false
|
||||
silent: false,
|
||||
});
|
||||
|
||||
const option = computed((): EChartsOption => {
|
||||
if (!props.data) return {};
|
||||
if (isUndefined(analyzer.logicData.value)) return {};
|
||||
|
||||
const channelCount = props.data.y.length;
|
||||
const channelCount = analyzer.logicData.value.y.length;
|
||||
const channelSpacing = 2; // 每个通道之间的间距
|
||||
|
||||
// 使用单个网格
|
||||
|
@ -100,9 +124,12 @@ const option = computed((): EChartsOption => {
|
|||
{
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
data: props.data!.x.map((x) => x.toFixed(3)),
|
||||
data: analyzer.logicData.value.x.map((x) => x.toFixed(3)),
|
||||
axisLabel: {
|
||||
formatter: (value: string) => `${value}${props.data!.xUnit}`,
|
||||
formatter: (value: string) =>
|
||||
analyzer.logicData.value
|
||||
? `${value}${analyzer.logicData.value.xUnit}`
|
||||
: `${value}`,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -118,7 +145,7 @@ const option = computed((): EChartsOption => {
|
|||
formatter: (value: number) => {
|
||||
const channelIndex = Math.round(value / channelSpacing);
|
||||
return channelIndex < channelCount
|
||||
? props.data!.channelNames[channelIndex]
|
||||
? analyzer.channelNames.value[channelIndex]
|
||||
: "";
|
||||
},
|
||||
},
|
||||
|
@ -127,10 +154,13 @@ const option = computed((): EChartsOption => {
|
|||
];
|
||||
|
||||
// 创建系列数据,每个通道有不同的Y偏移
|
||||
const series: LineSeriesOption[] = props.data.y.map((channelData, index) => ({
|
||||
name: props.data!.channelNames[index],
|
||||
const series: LineSeriesOption[] = analyzer.logicData.value.y.map(
|
||||
(channelData: number[], index: number) => ({
|
||||
name: analyzer.channelNames.value[index],
|
||||
type: "line",
|
||||
data: channelData.map((value) => value + index * channelSpacing + 0.2),
|
||||
data: channelData.map(
|
||||
(value: number) => value + index * channelSpacing + 0.2,
|
||||
),
|
||||
step: "end",
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
|
@ -147,7 +177,8 @@ const option = computed((): EChartsOption => {
|
|||
// progressive: 2000,
|
||||
// 减少动画以避免闪烁
|
||||
animation: false,
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
// 全局动画配置
|
||||
|
@ -164,16 +195,21 @@ const option = computed((): EChartsOption => {
|
|||
},
|
||||
formatter: (params: any) => {
|
||||
if (Array.isArray(params) && params.length > 0) {
|
||||
const timeValue = props.data!.x[params[0].dataIndex];
|
||||
const timeValue = analyzer.logicData.value!.x[params[0].dataIndex];
|
||||
const dataIndex = params[0].dataIndex;
|
||||
|
||||
let tooltip = `Time: ${timeValue.toFixed(3)}${props.data!.xUnit}<br/>`;
|
||||
let tooltip = `Time: ${timeValue.toFixed(3)}${analyzer.logicData.value!.xUnit}<br/>`;
|
||||
|
||||
// 显示所有通道在当前时间点的原始数值(0或1)
|
||||
props.data!.channelNames.forEach((channelName, index) => {
|
||||
const originalValue = props.data!.y[index][dataIndex];
|
||||
if (analyzer.logicData.value) {
|
||||
analyzer.channelNames.value.forEach(
|
||||
(channelName: string, index: number) => {
|
||||
const originalValue =
|
||||
analyzer.logicData.value!.y[index][dataIndex];
|
||||
tooltip += `${channelName}: ${originalValue}<br/>`;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
@ -207,4 +243,5 @@ const option = computed((): EChartsOption => {
|
|||
series: series,
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,84 +1,53 @@
|
|||
<template>
|
||||
<div class="space-y-4">
|
||||
<!-- 全局触发模式配置 -->
|
||||
<div class="space-y-6">
|
||||
<!-- 通道状态概览 -->
|
||||
<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="form-control">
|
||||
<!-- 全局触发模式选择 -->
|
||||
<div class="flex flex-row justify-between my-4 mx-2">
|
||||
<div class="flex flex-row gap-4">
|
||||
<label class="label">
|
||||
<span class="label-text font-medium">全局触发模式</span>
|
||||
<span class="label-text text-sm">全局触发逻辑</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"
|
||||
v-model="currentGlobalMode"
|
||||
@change="setGlobalMode(currentGlobalMode)"
|
||||
class="select select-sm select-bordered w-full"
|
||||
>
|
||||
<option
|
||||
v-for="val in signalValues"
|
||||
:key="val.value"
|
||||
:value="val.value"
|
||||
v-for="mode in globalModes"
|
||||
:key="mode.value"
|
||||
:value="mode.value"
|
||||
>
|
||||
{{ val.label }}
|
||||
{{ mode.label }} - {{ mode.description }}
|
||||
</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">
|
||||
<div class="flex flex-row gap-4">
|
||||
<button @click="toggleAllChannels" class="btn btn-primary btn-sm">
|
||||
{{ enabledChannelCount > 0 ? "全部禁用" : "全部启用" }}
|
||||
</button>
|
||||
<button
|
||||
@click="applyConfiguration"
|
||||
:disabled="isApplying"
|
||||
|
@ -95,172 +64,313 @@
|
|||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 状态指示 -->
|
||||
</div>
|
||||
<!-- 通道列表 -->
|
||||
<div class="space-y-2">
|
||||
<!-- 表头 - 小屏幕单列时显示 -->
|
||||
<div
|
||||
v-if="lastApplyResult"
|
||||
class="alert alert-sm"
|
||||
:class="lastApplyResult.success ? 'alert-success' : 'alert-error'"
|
||||
class="flex items-center gap-2 p-2 bg-base-300 rounded-lg text-sm font-medium lg:hidden"
|
||||
>
|
||||
<span>{{ lastApplyResult.message }}</span>
|
||||
<span class="w-16">通道</span>
|
||||
<span class="w-20">启用/触发</span>
|
||||
<span class="w-32">标签</span>
|
||||
<span class="w-16">颜色</span>
|
||||
<span class="w-32">触发操作</span>
|
||||
<span class="w-32">触发值</span>
|
||||
</div>
|
||||
|
||||
<!-- 通道配置网格 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<!-- 左列 (CH0-CH3) -->
|
||||
<div class="space-y-2">
|
||||
<!-- 左列表头 - 大屏幕时显示 -->
|
||||
<div
|
||||
class="hidden lg:flex items-center gap-2 p-2 bg-base-300 rounded-lg text-sm font-medium"
|
||||
>
|
||||
<span class="w-16">通道</span>
|
||||
<span class="w-20">启用</span>
|
||||
<span class="w-32">标签</span>
|
||||
<span class="w-16">颜色</span>
|
||||
<span class="w-32">触发操作</span>
|
||||
<span class="w-32">触发值</span>
|
||||
</div>
|
||||
|
||||
<!-- 左列通道 (0-3) -->
|
||||
<div
|
||||
v-for="(channel, index) in channels.slice(0, 4)"
|
||||
:key="index"
|
||||
class="flex items-center gap-2 p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
|
||||
>
|
||||
<!-- 通道编号和颜色指示 -->
|
||||
<div class="flex items-center gap-2 w-16">
|
||||
<span class="font-mono font-medium">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 w-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="channel.enabled"
|
||||
class="toggle toggle-sm toggle-primary"
|
||||
/>
|
||||
</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 w-16">
|
||||
<input
|
||||
type="color"
|
||||
v-model="channel.color"
|
||||
class="w-8 h-8 rounded border-2 border-base-300 cursor-pointer"
|
||||
:disabled="!channel.enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 触发操作符选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index].operator"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="op in operators"
|
||||
:key="op.value"
|
||||
:value="op.value"
|
||||
>
|
||||
{{ op.label }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- 触发信号值选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index].value"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="val in signalValues"
|
||||
:key="val.value"
|
||||
:value="val.value"
|
||||
>
|
||||
{{ val.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右列 (CH4-CH7) - 仅在大屏幕显示 -->
|
||||
<div class="hidden lg:block space-y-2">
|
||||
<!-- 右列表头 -->
|
||||
<div
|
||||
class="flex items-center gap-2 p-2 bg-base-300 rounded-lg text-sm font-medium"
|
||||
>
|
||||
<span class="w-16">通道</span>
|
||||
<span class="w-20">启用/触发</span>
|
||||
<span class="w-32">标签</span>
|
||||
<span class="w-16">颜色</span>
|
||||
<span class="w-32">触发操作</span>
|
||||
<span class="w-32">触发值</span>
|
||||
</div>
|
||||
|
||||
<!-- 右列通道 (4-7) -->
|
||||
<div
|
||||
v-for="(channel, index) in channels.slice(4, 8)"
|
||||
:key="index + 4"
|
||||
class="flex items-center gap-2 p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
|
||||
>
|
||||
<!-- 通道编号和颜色指示 -->
|
||||
<div class="flex items-center gap-2 w-16">
|
||||
<span class="font-mono font-medium">CH{{ index + 4 }}</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 w-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="channel.enabled"
|
||||
class="toggle toggle-sm toggle-primary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 通道标签 -->
|
||||
<div class="form-control w-32">
|
||||
<input
|
||||
type="text"
|
||||
v-model="channel.label"
|
||||
:placeholder="`通道 ${index + 4}`"
|
||||
class="input input-sm input-bordered w-full"
|
||||
:disabled="!channel.enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 颜色选择 -->
|
||||
<div class="form-control w-16">
|
||||
<input
|
||||
type="color"
|
||||
v-model="channel.color"
|
||||
class="w-8 h-8 rounded border-2 border-base-300 cursor-pointer"
|
||||
:disabled="!channel.enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 触发操作符选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index + 4].operator"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="op in operators"
|
||||
:key="op.value"
|
||||
:value="op.value"
|
||||
>
|
||||
{{ op.label }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- 触发信号值选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index + 4].value"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="val in signalValues"
|
||||
:key="val.value"
|
||||
:value="val.value"
|
||||
>
|
||||
{{ val.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 小屏幕时继续显示 CH4-CH7 -->
|
||||
<div class="lg:hidden space-y-2">
|
||||
<div
|
||||
v-for="(channel, index) in channels.slice(4, 8)"
|
||||
:key="index + 4"
|
||||
class="flex items-center gap-2 p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
|
||||
>
|
||||
<!-- 通道编号和颜色指示 -->
|
||||
<div class="flex items-center gap-2 w-16">
|
||||
<span class="font-mono font-medium">CH{{ index + 4 }}</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 w-20">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="channel.enabled"
|
||||
class="toggle toggle-sm toggle-primary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 通道标签 -->
|
||||
<div class="form-control w-32">
|
||||
<input
|
||||
type="text"
|
||||
v-model="channel.label"
|
||||
:placeholder="`通道 ${index + 4}`"
|
||||
class="input input-sm input-bordered w-full"
|
||||
:disabled="!channel.enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 颜色选择 -->
|
||||
<div class="form-control w-16">
|
||||
<input
|
||||
type="color"
|
||||
v-model="channel.color"
|
||||
class="w-8 h-8 rounded border-2 border-base-300 cursor-pointer"
|
||||
:disabled="!channel.enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 触发操作符选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index + 4].operator"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="op in operators"
|
||||
:key="op.value"
|
||||
:value="op.value"
|
||||
>
|
||||
{{ op.label }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<!-- 触发信号值选择 -->
|
||||
<select
|
||||
v-model="signalConfigs[index + 4].value"
|
||||
class="select select-sm select-bordered w-32"
|
||||
:disabled="!channel.enabled"
|
||||
>
|
||||
<option
|
||||
v-for="val in signalValues"
|
||||
:key="val.value"
|
||||
:value="val.value"
|
||||
>
|
||||
{{ val.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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";
|
||||
import { useRequiredInjection } from "@/utils/Common";
|
||||
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
|
||||
|
||||
// 使用设备配置
|
||||
const equipments = useEquipments();
|
||||
const {
|
||||
currentGlobalMode,
|
||||
isApplying,
|
||||
channels,
|
||||
signalConfigs,
|
||||
enabledChannelCount,
|
||||
globalModes,
|
||||
operators,
|
||||
signalValues,
|
||||
enableAllChannels,
|
||||
disableAllChannels,
|
||||
setGlobalMode,
|
||||
applyConfiguration,
|
||||
resetConfiguration,
|
||||
} = useRequiredInjection(useLogicAnalyzerState);
|
||||
|
||||
// 当前全局模式
|
||||
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}`,
|
||||
};
|
||||
const toggleAllChannels = () => {
|
||||
if (enabledChannelCount.value > 0) {
|
||||
disableAllChannels();
|
||||
} else {
|
||||
throw new Error("设置失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("设置全局触发模式失败:", error);
|
||||
lastApplyResult.value = {
|
||||
success: false,
|
||||
message: "设置全局触发模式失败",
|
||||
};
|
||||
enableAllChannels();
|
||||
}
|
||||
};
|
||||
|
||||
// 应用完整配置
|
||||
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>
|
||||
|
|
|
@ -1,78 +1,5 @@
|
|||
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 { LogicalWaveFormDisplay };
|
||||
export { default as TriggerSettings } from './TriggerSettings.vue'
|
||||
export { default as ChannelConfig } from './ChannelConfig.vue'
|
||||
export { useProvideLogicAnalyzer , useLogicAnalyzerState } from './LogicAnalyzerManager.ts'
|
|
@ -1,18 +1,25 @@
|
|||
<template>
|
||||
<div class="bg-base-100 flex flex-col">
|
||||
<div class="bg-base-100 flex flex-col gap-10 mb-5">
|
||||
<!-- 逻辑信号展示 -->
|
||||
<div class="card bg-base-200 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
<h2 class="card-title flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<Zap class="w-5 h-5" />
|
||||
逻辑信号分析
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="btn btn-sm btn-error" @click="handleDeleteData">
|
||||
清空
|
||||
</button>
|
||||
</div>
|
||||
</h2>
|
||||
<LogicalWaveFormDisplay :data="generateTestLogicData()" />
|
||||
<LogicalWaveFormDisplay />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 触发设置 -->
|
||||
<div class="card bg-base-200 shadow-xl mt-4">
|
||||
<div class="card bg-base-200 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
<Settings class="w-5 h-5" />
|
||||
|
@ -21,17 +28,6 @@
|
|||
<TriggerSettings />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通道配置 -->
|
||||
<div class="card bg-base-200 shadow-xl mt-4">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">
|
||||
<Layers class="w-5 h-5" />
|
||||
通道配置
|
||||
</h2>
|
||||
<ChannelConfig />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -42,8 +38,14 @@ import {
|
|||
LogicalWaveFormDisplay,
|
||||
generateTestLogicData,
|
||||
TriggerSettings,
|
||||
ChannelConfig
|
||||
} from "@/components/LogicAnalyzer";
|
||||
import { useProvideLogicAnalyzer } from "@/components/LogicAnalyzer";
|
||||
|
||||
const analyzer = useProvideLogicAnalyzer();
|
||||
|
||||
function handleDeleteData() {
|
||||
analyzer.logicData.value = undefined;
|
||||
}
|
||||
|
||||
// 使用全局设备配置
|
||||
const equipments = useEquipments();
|
||||
|
|
Loading…
Reference in New Issue