From 6302489f3a4f0267da39557b910d261e18e0d82a Mon Sep 17 00:00:00 2001 From: SikongJueluo <1822250894@qq.com> Date: Fri, 22 Aug 2025 04:05:00 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=A4=BA=E6=B3=A2?= =?UTF-8?q?=E5=99=A8=E6=8E=A2=E6=B5=8B=E5=8F=82=E6=95=B0=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=97=8B=E8=BD=AC=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E5=99=A8=E6=8C=89=E4=B8=8B=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Controllers/OscilloscopeController.cs | 8 +- server/src/Hubs/OscilloscopeHub.cs | 24 ++-- server/src/Hubs/RotaryEncoderHub.cs | 25 ++++ server/src/Peripherals/RotaryEncoderClient.cs | 47 ++++++- .../Oscilloscope/OscilloscopeManager.ts | 18 ++- .../OscilloscopeWaveformDisplay.vue | 131 +++++++++++++++++- .../equipments/EC11RotaryEncoder.vue | 19 ++- src/components/equipments/Switch.vue | 13 +- src/stores/Peripherals/RotaryEncoder.ts | 11 +- .../Peripherals.RotaryEncoderClient.ts | 6 + .../signalR/TypedSignalR.Client/index.ts | 6 +- .../TypedSignalR.Client/server.Hubs.ts | 8 +- src/utils/signalR/server.Hubs.ts | 8 +- src/views/Exam/ExamEditModal.vue | 9 +- 14 files changed, 278 insertions(+), 55 deletions(-) diff --git a/server/src/Controllers/OscilloscopeController.cs b/server/src/Controllers/OscilloscopeController.cs index 85b57cb..5af71bf 100644 --- a/server/src/Controllers/OscilloscopeController.cs +++ b/server/src/Controllers/OscilloscopeController.cs @@ -268,10 +268,10 @@ public class OscilloscopeApiController : ControllerBase var response = new OscilloscopeDataResponse { - ADFrequency = freqResult.Value, - ADVpp = vppResult.Value, - ADMax = maxResult.Value, - ADMin = minResult.Value, + AdFrequency = freqResult.Value, + AdVpp = vppResult.Value, + AdMax = maxResult.Value, + AdMin = minResult.Value, WaveformData = Convert.ToBase64String(waveformResult.Value) }; diff --git a/server/src/Hubs/OscilloscopeHub.cs b/server/src/Hubs/OscilloscopeHub.cs index ecf908f..3d495c2 100644 --- a/server/src/Hubs/OscilloscopeHub.cs +++ b/server/src/Hubs/OscilloscopeHub.cs @@ -34,10 +34,10 @@ public interface IOscilloscopeReceiver [TranspilationSource] public class OscilloscopeDataResponse { - public uint ADFrequency { get; set; } - public byte ADVpp { get; set; } - public byte ADMax { get; set; } - public byte ADMin { get; set; } + public uint AdFrequency { get; set; } + public byte AdVpp { get; set; } + public byte AdMax { get; set; } + public byte AdMin { get; set; } public string WaveformData { get; set; } = ""; } @@ -275,19 +275,19 @@ public class OscilloscopeHub : Hub, IOscilloscopeHub var response = new OscilloscopeDataResponse { - ADFrequency = freqResult.Value, - ADVpp = vppResult.Value, - ADMax = maxResult.Value, - ADMin = minResult.Value, + AdFrequency = freqResult.Value, + AdVpp = vppResult.Value, + AdMax = maxResult.Value, + AdMin = minResult.Value, WaveformData = Convert.ToBase64String(waveformResult.Value) }; return new OscilloscopeDataResponse { - ADFrequency = freqResult.Value, - ADVpp = vppResult.Value, - ADMax = maxResult.Value, - ADMin = minResult.Value, + AdFrequency = freqResult.Value, + AdVpp = vppResult.Value, + AdMax = maxResult.Value, + AdMin = minResult.Value, WaveformData = Convert.ToBase64String(waveformResult.Value) }; } diff --git a/server/src/Hubs/RotaryEncoderHub.cs b/server/src/Hubs/RotaryEncoderHub.cs index 54d5a5c..d097228 100644 --- a/server/src/Hubs/RotaryEncoderHub.cs +++ b/server/src/Hubs/RotaryEncoderHub.cs @@ -16,6 +16,7 @@ public interface IRotaryEncoderHub { Task SetEnable(bool enable); Task RotateEncoderOnce(int num, RotaryEncoderDirection direction); + Task PressEncoderOnce(int num, RotaryEncoderPressStatus press); Task EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq); Task DisableCycleRotateEncoder(); } @@ -133,6 +134,30 @@ public class RotaryEncoderHub : Hub, IRotaryEncoderHub } } + public async Task PressEncoderOnce(int num, RotaryEncoderPressStatus press) + { + try + { + if (num <= 0 || num > 4) + throw new ArgumentException($"RotaryEncoder num should be 1~3, instead of {num}"); + + var board = TryGetBoard().OrThrow(() => new Exception("Board not found")); + var encoderCtrl = new RotaryEncoderCtrl(board.IpAddr, board.Port, 0); + var result = await encoderCtrl.PressEncoderOnce(num, press); + if (!result.IsSuccessful) + { + logger.Error(result.Error, $"RotateEncoderOnce({num}, {press}) failed"); + return false; + } + return result.Value; + } + catch (Exception ex) + { + logger.Error(ex, "Failed to rotate encoder once"); + return false; + } + } + public async Task EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq) { try diff --git a/server/src/Peripherals/RotaryEncoderClient.cs b/server/src/Peripherals/RotaryEncoderClient.cs index 02f4798..9fa212c 100644 --- a/server/src/Peripherals/RotaryEncoderClient.cs +++ b/server/src/Peripherals/RotaryEncoderClient.cs @@ -7,8 +7,10 @@ namespace Peripherals.RotaryEncoderClient; class RotaryEncoderCtrlAddr { public const UInt32 BASE = 0xB0_00_00_30; + public const UInt32 PRESS_BASE = 0xB0_00_00_40; public const UInt32 ENABLE = BASE; + public const UInt32 PRESS_ENABLE = PRESS_BASE; } [TranspilationSource] @@ -18,6 +20,13 @@ public enum RotaryEncoderDirection : uint Clockwise = 1, } +[TranspilationSource] +public enum RotaryEncoderPressStatus : uint +{ + Press = 0, + Release = 1, +} + public class RotaryEncoderCtrl { private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); @@ -45,10 +54,22 @@ public class RotaryEncoderCtrl MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); else return new(new Exception("Message Bus not work!")); - var ret = await UDPClientPool.WriteAddr( - this.ep, this.taskID, RotaryEncoderCtrlAddr.ENABLE, enable ? 0x1U : 0x0U, this.timeout); - if (!ret.IsSuccessful) return new(ret.Error); - return ret.Value; + { + var ret = await UDPClientPool.WriteAddr( + this.ep, this.taskID, RotaryEncoderCtrlAddr.ENABLE, enable ? 0x1U : 0x0U, this.timeout); + if (!ret.IsSuccessful) return new(ret.Error); + if (!ret.Value) + { + logger.Error($"Set Rotary Encoder Enable failed: {ret.Error}"); + return false; + } + } + { + var ret = await UDPClientPool.WriteAddr( + this.ep, this.taskID, RotaryEncoderCtrlAddr.PRESS_ENABLE, enable ? 0x1U : 0x0U, this.timeout); + if (!ret.IsSuccessful) return new(ret.Error); + return ret.Value; + } } public async ValueTask> RotateEncoderOnce(int num, RotaryEncoderDirection direction) @@ -61,7 +82,23 @@ public class RotaryEncoderCtrl this.ep, this.taskID, RotaryEncoderCtrlAddr.BASE + (UInt32)num, (UInt32)direction, this.timeout); if (!ret.IsSuccessful) { - logger.Error($"Set Rotary Encoder {num} {direction.ToString()} failed: {ret.Error}"); + logger.Error($"Set Rotary Encoder Rotate {num} {direction.ToString()} failed: {ret.Error}"); + return new(ret.Error); + } + return ret.Value; + } + + public async ValueTask> PressEncoderOnce(int num, RotaryEncoderPressStatus press) + { + if (MsgBus.IsRunning) + MsgBus.UDPServer.ClearUDPData(this.address, this.taskID); + else return new(new Exception("Message Bus not work!")); + + var ret = await UDPClientPool.WriteAddr( + this.ep, this.taskID, RotaryEncoderCtrlAddr.PRESS_BASE + (UInt32)num, (UInt32)press, this.timeout); + if (!ret.IsSuccessful) + { + logger.Error($"Set Rotary Encoder Set {num} {press.ToString()} failed: {ret.Error}"); return new(ret.Error); } return ret.Value; diff --git a/src/components/Oscilloscope/OscilloscopeManager.ts b/src/components/Oscilloscope/OscilloscopeManager.ts index d63abf4..7cccc5f 100644 --- a/src/components/Oscilloscope/OscilloscopeManager.ts +++ b/src/components/Oscilloscope/OscilloscopeManager.ts @@ -199,10 +199,16 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState( } sampleCount.value = bytes.length; + const aDFrequency = resp.adFrequency; + + // 计算采样周期(ns) + const samplePeriodNs = + aDFrequency > 0 ? 1_000_000_000 / aDFrequency : 200; + // 构建时间轴 const x = Array.from( { length: bytes.length }, - (_, i) => (i * samplePeriodNs.value) / 1000, // us + (_, i) => (i * samplePeriodNs) / 1000, // us ); const y = Array.from(bytes); @@ -211,11 +217,13 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState( y, xUnit: "us", yUnit: "V", - adFrequency: resp.aDFrequency, - adVpp: resp.aDVpp, - adMax: resp.aDMax, - adMin: resp.aDMin, + adFrequency: aDFrequency, + adVpp: resp.adVpp, + adMax: resp.adMax, + adMin: resp.adMin, }; + + console.log("解析后的参数:", resp, oscData.value); // 添加调试日志 }; // 获取数据 diff --git a/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue b/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue index 7779138..1cbb83c 100644 --- a/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue +++ b/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue @@ -88,6 +88,67 @@
采集中 + + +
+

+ + 测量参数 +

+ +
+ +
+ 采样频率: + + {{ formatFrequency(oscData?.adFrequency || 0) }} + +
+ + +
+ Vpp: + + {{ (oscData?.adVpp || 0).toFixed(2) }}V + +
+ + +
+ 最大值: + + {{ formatAdcValue(oscData?.adMax || 0) }} + +
+ + +
+ 最小值: + + {{ formatAdcValue(oscData?.adMin || 0) }} + +
+ + +
+ 采样点: + + {{ formatSampleCount(oscManager.sampleCount.value) }} + +
+ + +
+ 周期: + + {{ formatPeriod(oscManager.samplePeriodNs.value) }} + +
+
+
@@ -156,6 +217,44 @@ const hasData = computed(() => { ); }); +// 格式化频率显示 +const formatFrequency = (frequency: number): string => { + if (frequency >= 1_000_000) { + return `${(frequency / 1_000_000).toFixed(1)}MHz`; + } else if (frequency >= 1_000) { + return `${(frequency / 1_000).toFixed(1)}kHz`; + } else { + return `${frequency}Hz`; + } +}; + +// 格式化ADC值显示 +const formatAdcValue = (value: number): string => { + return `${value} (${((value / 255) * 3.3).toFixed(2)}V)`; +}; + +// 格式化采样点数显示 +const formatSampleCount = (count: number): string => { + if (count >= 1_000_000) { + return `${(count / 1_000_000).toFixed(1)}M`; + } else if (count >= 1_000) { + return `${(count / 1_000).toFixed(1)}k`; + } else { + return `${count}`; + } +}; + +// 格式化周期显示 +const formatPeriod = (periodNs: number): string => { + if (periodNs >= 1_000_000) { + return `${(periodNs / 1_000_000).toFixed(2)}ms`; + } else if (periodNs >= 1_000) { + return `${(periodNs / 1_000).toFixed(2)}μs`; + } else { + return `${periodNs.toFixed(2)}ns`; + } +}; + const option = computed((): EChartsOption => { if (!oscData.value || !oscData.value.x || !oscData.value.y) { return {}; @@ -235,7 +334,9 @@ const option = computed((): EChartsOption => { if (!oscData.value) return ""; let result = `
时间: ${params[0].data[0].toFixed(2)} ${oscData.value.xUnit}
`; params.forEach((param: any) => { - result += `
● ${param.seriesName}: ${param.data[1].toFixed(3)} ${oscData.value?.yUnit ?? ""}
`; + const adcValue = param.data[1]; + const voltage = ((adcValue / 255) * 3.3).toFixed(3); + result += `
● ${param.seriesName}: ${adcValue} (${voltage}V)
`; }); return result; }, @@ -333,7 +434,7 @@ const option = computed((): EChartsOption => { }, yAxis: { type: "value", - name: oscData.value ? `电压 (${oscData.value.yUnit})` : "电压", + name: oscData.value ? `ADC值 (0-255)` : "ADC值", nameLocation: "middle", nameGap: 50, nameTextStyle: { @@ -357,6 +458,9 @@ const option = computed((): EChartsOption => { axisLabel: { color: "#64748B", fontSize: 11, + formatter: (value: number) => { + return `${value} (${((value / 255) * 3.3).toFixed(1)}V)`; + }, }, splitLine: { show: true, @@ -536,6 +640,14 @@ button:active::after { font-size: 12px; padding: 4px 8px; } + + /* 移动端测量面板调整 */ + .absolute.top-4.left-4 { + top: 8px; + left: 8px; + min-width: 180px; + font-size: 11px; + } } /* 平滑过渡效果 */ @@ -548,4 +660,17 @@ button:focus-visible { outline: 2px solid rgba(59, 130, 246, 0.5); outline-offset: 2px; } - + +/* 测量面板样式增强 */ +.absolute.top-4.left-4 { + backdrop-filter: blur(8px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease-in-out; + z-index: 10; +} + +.absolute.top-4.left-4:hover { + transform: translateY(-1px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); +} + \ No newline at end of file diff --git a/src/components/equipments/EC11RotaryEncoder.vue b/src/components/equipments/EC11RotaryEncoder.vue index 29f5a69..0e0bf9c 100644 --- a/src/components/equipments/EC11RotaryEncoder.vue +++ b/src/components/equipments/EC11RotaryEncoder.vue @@ -176,7 +176,10 @@ diff --git a/src/stores/Peripherals/RotaryEncoder.ts b/src/stores/Peripherals/RotaryEncoder.ts index 79dc748..5f4b5f7 100644 --- a/src/stores/Peripherals/RotaryEncoder.ts +++ b/src/stores/Peripherals/RotaryEncoder.ts @@ -1,5 +1,8 @@ import { AuthManager } from "@/utils/AuthManager"; -import type { RotaryEncoderDirection } from "@/utils/signalR/Peripherals.RotaryEncoderClient"; +import type { + RotaryEncoderDirection, + RotaryEncoderPressStatus, +} from "@/utils/signalR/Peripherals.RotaryEncoderClient"; import { getHubProxyFactory, getReceiverRegister, @@ -72,6 +75,11 @@ export const useRotaryEncoder = defineStore("RotaryEncoder", () => { return await proxy.rotateEncoderOnce(num, direction); } + async function pressOnce(num: number, pressStatus: RotaryEncoderPressStatus) { + const proxy = getHubProxy(); + return await proxy.pressEncoderOnce(num, pressStatus); + } + async function enableCycleRotate( num: number, direction: RotaryEncoderDirection, @@ -89,6 +97,7 @@ export const useRotaryEncoder = defineStore("RotaryEncoder", () => { return { setEnable, rotateOnce, + pressOnce, enableCycleRotate, disableCycleRotate, }; diff --git a/src/utils/signalR/Peripherals.RotaryEncoderClient.ts b/src/utils/signalR/Peripherals.RotaryEncoderClient.ts index bf97483..b9aed48 100644 --- a/src/utils/signalR/Peripherals.RotaryEncoderClient.ts +++ b/src/utils/signalR/Peripherals.RotaryEncoderClient.ts @@ -8,3 +8,9 @@ export enum RotaryEncoderDirection { Clockwise = 1, } +/** Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderPressStatus */ +export enum RotaryEncoderPressStatus { + Press = 0, + Release = 1, +} + diff --git a/src/utils/signalR/TypedSignalR.Client/index.ts b/src/utils/signalR/TypedSignalR.Client/index.ts index c2b5ba7..1602bb7 100644 --- a/src/utils/signalR/TypedSignalR.Client/index.ts +++ b/src/utils/signalR/TypedSignalR.Client/index.ts @@ -5,7 +5,7 @@ import type { HubConnection, IStreamResult, Subject } from '@microsoft/signalr'; import type { IDigitalTubesHub, IJtagHub, IOscilloscopeHub, IProgressHub, IRotaryEncoderHub, IWS2812Hub, IDigitalTubesReceiver, IJtagReceiver, IOscilloscopeReceiver, IProgressReceiver, IRotaryEncoderReceiver, IWS2812Receiver } from './server.Hubs'; import type { DigitalTubeTaskStatus, OscilloscopeFullConfig, OscilloscopeDataResponse, ProgressInfo } from '../server.Hubs'; -import type { RotaryEncoderDirection } from '../Peripherals.RotaryEncoderClient'; +import type { RotaryEncoderDirection, RotaryEncoderPressStatus } from '../Peripherals.RotaryEncoderClient'; import type { RGBColor } from '../Peripherals.WS2812Client'; @@ -270,6 +270,10 @@ class IRotaryEncoderHub_HubProxy implements IRotaryEncoderHub { return await this.connection.invoke("RotateEncoderOnce", num, direction); } + public readonly pressEncoderOnce = async (num: number, press: RotaryEncoderPressStatus): Promise => { + return await this.connection.invoke("PressEncoderOnce", num, press); + } + public readonly enableCycleRotateEncoder = async (num: number, direction: RotaryEncoderDirection, freq: number): Promise => { return await this.connection.invoke("EnableCycleRotateEncoder", num, direction, freq); } diff --git a/src/utils/signalR/TypedSignalR.Client/server.Hubs.ts b/src/utils/signalR/TypedSignalR.Client/server.Hubs.ts index b26a302..6cb2ba5 100644 --- a/src/utils/signalR/TypedSignalR.Client/server.Hubs.ts +++ b/src/utils/signalR/TypedSignalR.Client/server.Hubs.ts @@ -4,7 +4,7 @@ // @ts-nocheck import type { IStreamResult, Subject } from '@microsoft/signalr'; import type { DigitalTubeTaskStatus, OscilloscopeFullConfig, OscilloscopeDataResponse, ProgressInfo } from '../server.Hubs'; -import type { RotaryEncoderDirection } from '../Peripherals.RotaryEncoderClient'; +import type { RotaryEncoderDirection, RotaryEncoderPressStatus } from '../Peripherals.RotaryEncoderClient'; import type { RGBColor } from '../Peripherals.WS2812Client'; export type IDigitalTubesHub = { @@ -116,6 +116,12 @@ export type IRotaryEncoderHub = { rotateEncoderOnce(num: number, direction: RotaryEncoderDirection): Promise; /** * @param num Transpiled from int + * @param press Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderPressStatus + * @returns Transpiled from System.Threading.Tasks.Task + */ + pressEncoderOnce(num: number, press: RotaryEncoderPressStatus): Promise; + /** + * @param num Transpiled from int * @param direction Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderDirection * @param freq Transpiled from int * @returns Transpiled from System.Threading.Tasks.Task diff --git a/src/utils/signalR/server.Hubs.ts b/src/utils/signalR/server.Hubs.ts index d6d84b7..8597c40 100644 --- a/src/utils/signalR/server.Hubs.ts +++ b/src/utils/signalR/server.Hubs.ts @@ -13,13 +13,13 @@ export type DigitalTubeTaskStatus = { /** Transpiled from server.Hubs.OscilloscopeDataResponse */ export type OscilloscopeDataResponse = { /** Transpiled from uint */ - aDFrequency: number; + adFrequency: number; /** Transpiled from byte */ - aDVpp: number; + adVpp: number; /** Transpiled from byte */ - aDMax: number; + adMax: number; /** Transpiled from byte */ - aDMin: number; + adMin: number; /** Transpiled from string */ waveformData: string; } diff --git a/src/views/Exam/ExamEditModal.vue b/src/views/Exam/ExamEditModal.vue index db0a4da..5f007c1 100644 --- a/src/views/Exam/ExamEditModal.vue +++ b/src/views/Exam/ExamEditModal.vue @@ -182,7 +182,7 @@
MD文档 (可选)
{ editExamInfo.value.id.trim() !== "" && editExamInfo.value.name.trim() !== "" && editExamInfo.value.description.trim() !== "" && - (uploadFiles.value.mdFile !== null || mode.value === "edit") + (mode.value === "edit") ); }); @@ -605,11 +605,6 @@ const submitCreateExam = async () => { return; } - if (!uploadFiles.value.mdFile) { - alert.error("请上传MD文档"); - return; - } - isUpdating.value = true; try {