From 9f25391540a5db70b4c3b854d1a3f077437f8ff3 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Tue, 15 Jul 2025 18:30:18 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=88=86=E6=9E=90=E4=BB=AA=E5=89=8D=E7=AB=AF=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LogicAnalyzer/ChannelConfig.vue | 174 ------ .../LogicAnalyzer/LogicAnalyzerManager.ts | 224 +++++++ .../LogicAnalyzer/LogicalWaveFormDisplay.vue | 141 +++-- .../LogicAnalyzer/TriggerSettings.vue | 590 +++++++++++------- src/components/LogicAnalyzer/index.ts | 3 +- src/views/Project/LogicAnalyzer.vue | 42 +- 6 files changed, 690 insertions(+), 484 deletions(-) delete mode 100644 src/components/LogicAnalyzer/ChannelConfig.vue diff --git a/src/components/LogicAnalyzer/ChannelConfig.vue b/src/components/LogicAnalyzer/ChannelConfig.vue deleted file mode 100644 index 785f340..0000000 --- a/src/components/LogicAnalyzer/ChannelConfig.vue +++ /dev/null @@ -1,174 +0,0 @@ - - - diff --git a/src/components/LogicAnalyzer/LogicAnalyzerManager.ts b/src/components/LogicAnalyzer/LogicAnalyzerManager.ts index e69de29..4b0cacb 100644 --- a/src/components/LogicAnalyzer/LogicAnalyzerManager.ts +++ b/src/components/LogicAnalyzer/LogicAnalyzerManager.ts @@ -0,0 +1,224 @@ +import { createInjectionState } from "@vueuse/core"; +import { generateTestLogicData, type LogicDataType } from "."; +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 interface 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(); + const alert = useAlertStore(); + + // 触发设置相关状态 + const currentGlobalMode = ref(GlobalCaptureMode.AND); + const isApplying = ref(false); + + // 通道配置 + const channels = reactive( + 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 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); + }; + + return { + // 原有的逻辑数据 + logicData, + + // 触发设置状态 + currentGlobalMode, + isApplying, + channels, + signalConfigs, + enabledChannelCount, + + // 选项数据 + globalModes, + operators, + signalValues, + + // 触发设置方法 + enableAllChannels, + disableAllChannels, + setGlobalMode, + applyConfiguration, + resetConfiguration, + }; + }, +); + +export { useProvideLogicAnalyzer, useLogicAnalyzerState }; diff --git a/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue b/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue index ae2780e..ec5d176 100644 --- a/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue +++ b/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue @@ -1,17 +1,43 @@ @@ -19,7 +45,8 @@ diff --git a/src/components/LogicAnalyzer/TriggerSettings.vue b/src/components/LogicAnalyzer/TriggerSettings.vue index 7102045..48e6a83 100644 --- a/src/components/LogicAnalyzer/TriggerSettings.vue +++ b/src/components/LogicAnalyzer/TriggerSettings.vue @@ -1,266 +1,376 @@ diff --git a/src/components/LogicAnalyzer/index.ts b/src/components/LogicAnalyzer/index.ts index 8efbb07..c72ae2b 100644 --- a/src/components/LogicAnalyzer/index.ts +++ b/src/components/LogicAnalyzer/index.ts @@ -1,3 +1,4 @@ +import { exp } from "mathjs"; import LogicalWaveFormDisplay from "./LogicalWaveFormDisplay.vue"; type LogicDataType = { @@ -75,4 +76,4 @@ function generateTestLogicData(): LogicDataType { export { LogicalWaveFormDisplay, generateTestLogicData, type LogicDataType }; export { default as TriggerSettings } from './TriggerSettings.vue' -export { default as ChannelConfig } from './ChannelConfig.vue' \ No newline at end of file +export { useProvideLogicAnalyzer , useLogicAnalyzerState } from './LogicAnalyzerManager.ts' \ No newline at end of file diff --git a/src/views/Project/LogicAnalyzer.vue b/src/views/Project/LogicAnalyzer.vue index 28d6b9b..3173cfc 100644 --- a/src/views/Project/LogicAnalyzer.vue +++ b/src/views/Project/LogicAnalyzer.vue @@ -1,18 +1,25 @@ diff --git a/src/components/LogicAnalyzer/index.ts b/src/components/LogicAnalyzer/index.ts index c72ae2b..3242f1c 100644 --- a/src/components/LogicAnalyzer/index.ts +++ b/src/components/LogicAnalyzer/index.ts @@ -1,79 +1,5 @@ -import { exp } from "mathjs"; 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 { useProvideLogicAnalyzer , useLogicAnalyzerState } from './LogicAnalyzerManager.ts' \ No newline at end of file From 4562be2d0108f18a7e053c9a4b7d7dd3c81c7b76 Mon Sep 17 00:00:00 2001 From: alivender <13898766233@163.com> Date: Tue, 15 Jul 2025 19:10:12 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84Camera=E5=AF=B9?= =?UTF-8?q?=E7=84=A6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/Peripherals/CameraClient.cs | 47 +++++++++++++++++++------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/server/src/Peripherals/CameraClient.cs b/server/src/Peripherals/CameraClient.cs index 2b1ff20..519c34c 100644 --- a/server/src/Peripherals/CameraClient.cs +++ b/server/src/Peripherals/CameraClient.cs @@ -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; } @@ -1290,6 +1293,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; } @@ -1313,10 +1342,7 @@ class Camera logger.Info("已发送单点对焦命令 (0x3022 = 0x03)"); // 步骤2: 读取寄存器 0x3029 的状态,如果返回值为 0x10,代表对焦已完成 - int iteration = 5000; - byte focusStatus = 0x00; - - do + for (int iteration = 5000; iteration > 0; iteration--) { var readResult = await ReadRegister(0x3029); if (!readResult.IsSuccessful) @@ -1325,23 +1351,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(10); } - while (focusStatus != 0x10); // 步骤3: 写寄存器 0x3022 为 0x06,暂停对焦过程,使镜头将保持在此对焦位置 var pauseResult = await ConfigureRegisters([[0x3022, 0x06]]); @@ -1351,7 +1374,7 @@ class Camera return pauseResult; } - logger.Info($"自动对焦完成并暂停,镜头保持在对焦位置,剩余迭代次数: {iteration}"); + logger.Info("自动对焦完成并暂停,镜头保持在对焦位置"); return true; }