diff --git a/server/src/Peripherals/CameraClient.cs b/server/src/Peripherals/CameraClient.cs index bf39a6a..790ae3d 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; } @@ -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; } 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..cdc196a 100644 --- a/src/components/LogicAnalyzer/LogicAnalyzerManager.ts +++ b/src/components/LogicAnalyzer/LogicAnalyzerManager.ts @@ -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(); + 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 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 }; diff --git a/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue b/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue index ae2780e..8d4073f 100644 --- a/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue +++ b/src/components/LogicAnalyzer/LogicalWaveFormDisplay.vue @@ -1,17 +1,43 @@ @@ -19,7 +45,7 @@ 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..3242f1c 100644 --- a/src/components/LogicAnalyzer/index.ts +++ b/src/components/LogicAnalyzer/index.ts @@ -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' \ 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 @@