feat: 增加示波器探测参数显示,增加旋转编码器按下的功能
This commit is contained in:
parent
7d3ef598de
commit
6302489f3a
|
@ -268,10 +268,10 @@ public class OscilloscopeApiController : ControllerBase
|
||||||
|
|
||||||
var response = new OscilloscopeDataResponse
|
var response = new OscilloscopeDataResponse
|
||||||
{
|
{
|
||||||
ADFrequency = freqResult.Value,
|
AdFrequency = freqResult.Value,
|
||||||
ADVpp = vppResult.Value,
|
AdVpp = vppResult.Value,
|
||||||
ADMax = maxResult.Value,
|
AdMax = maxResult.Value,
|
||||||
ADMin = minResult.Value,
|
AdMin = minResult.Value,
|
||||||
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,10 +34,10 @@ public interface IOscilloscopeReceiver
|
||||||
[TranspilationSource]
|
[TranspilationSource]
|
||||||
public class OscilloscopeDataResponse
|
public class OscilloscopeDataResponse
|
||||||
{
|
{
|
||||||
public uint ADFrequency { get; set; }
|
public uint AdFrequency { get; set; }
|
||||||
public byte ADVpp { get; set; }
|
public byte AdVpp { get; set; }
|
||||||
public byte ADMax { get; set; }
|
public byte AdMax { get; set; }
|
||||||
public byte ADMin { get; set; }
|
public byte AdMin { get; set; }
|
||||||
public string WaveformData { get; set; } = "";
|
public string WaveformData { get; set; } = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,19 +275,19 @@ public class OscilloscopeHub : Hub<IOscilloscopeReceiver>, IOscilloscopeHub
|
||||||
|
|
||||||
var response = new OscilloscopeDataResponse
|
var response = new OscilloscopeDataResponse
|
||||||
{
|
{
|
||||||
ADFrequency = freqResult.Value,
|
AdFrequency = freqResult.Value,
|
||||||
ADVpp = vppResult.Value,
|
AdVpp = vppResult.Value,
|
||||||
ADMax = maxResult.Value,
|
AdMax = maxResult.Value,
|
||||||
ADMin = minResult.Value,
|
AdMin = minResult.Value,
|
||||||
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
||||||
};
|
};
|
||||||
|
|
||||||
return new OscilloscopeDataResponse
|
return new OscilloscopeDataResponse
|
||||||
{
|
{
|
||||||
ADFrequency = freqResult.Value,
|
AdFrequency = freqResult.Value,
|
||||||
ADVpp = vppResult.Value,
|
AdVpp = vppResult.Value,
|
||||||
ADMax = maxResult.Value,
|
AdMax = maxResult.Value,
|
||||||
ADMin = minResult.Value,
|
AdMin = minResult.Value,
|
||||||
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
WaveformData = Convert.ToBase64String(waveformResult.Value)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ public interface IRotaryEncoderHub
|
||||||
{
|
{
|
||||||
Task<bool> SetEnable(bool enable);
|
Task<bool> SetEnable(bool enable);
|
||||||
Task<bool> RotateEncoderOnce(int num, RotaryEncoderDirection direction);
|
Task<bool> RotateEncoderOnce(int num, RotaryEncoderDirection direction);
|
||||||
|
Task<bool> PressEncoderOnce(int num, RotaryEncoderPressStatus press);
|
||||||
Task<bool> EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq);
|
Task<bool> EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq);
|
||||||
Task<bool> DisableCycleRotateEncoder();
|
Task<bool> DisableCycleRotateEncoder();
|
||||||
}
|
}
|
||||||
|
@ -133,6 +134,30 @@ public class RotaryEncoderHub : Hub<IRotaryEncoderReceiver>, IRotaryEncoderHub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> 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<bool> EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq)
|
public async Task<bool> EnableCycleRotateEncoder(int num, RotaryEncoderDirection direction, int freq)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -7,8 +7,10 @@ namespace Peripherals.RotaryEncoderClient;
|
||||||
class RotaryEncoderCtrlAddr
|
class RotaryEncoderCtrlAddr
|
||||||
{
|
{
|
||||||
public const UInt32 BASE = 0xB0_00_00_30;
|
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 ENABLE = BASE;
|
||||||
|
public const UInt32 PRESS_ENABLE = PRESS_BASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
[TranspilationSource]
|
[TranspilationSource]
|
||||||
|
@ -18,6 +20,13 @@ public enum RotaryEncoderDirection : uint
|
||||||
Clockwise = 1,
|
Clockwise = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TranspilationSource]
|
||||||
|
public enum RotaryEncoderPressStatus : uint
|
||||||
|
{
|
||||||
|
Press = 0,
|
||||||
|
Release = 1,
|
||||||
|
}
|
||||||
|
|
||||||
public class RotaryEncoderCtrl
|
public class RotaryEncoderCtrl
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
@ -45,10 +54,22 @@ public class RotaryEncoderCtrl
|
||||||
MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
MsgBus.UDPServer.ClearUDPData(this.address, this.taskID);
|
||||||
else return new(new Exception("Message Bus not work!"));
|
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);
|
var ret = await UDPClientPool.WriteAddr(
|
||||||
if (!ret.IsSuccessful) return new(ret.Error);
|
this.ep, this.taskID, RotaryEncoderCtrlAddr.ENABLE, enable ? 0x1U : 0x0U, this.timeout);
|
||||||
return ret.Value;
|
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<Result<bool>> RotateEncoderOnce(int num, RotaryEncoderDirection direction)
|
public async ValueTask<Result<bool>> RotateEncoderOnce(int num, RotaryEncoderDirection direction)
|
||||||
|
@ -61,7 +82,23 @@ public class RotaryEncoderCtrl
|
||||||
this.ep, this.taskID, RotaryEncoderCtrlAddr.BASE + (UInt32)num, (UInt32)direction, this.timeout);
|
this.ep, this.taskID, RotaryEncoderCtrlAddr.BASE + (UInt32)num, (UInt32)direction, this.timeout);
|
||||||
if (!ret.IsSuccessful)
|
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<Result<bool>> 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 new(ret.Error);
|
||||||
}
|
}
|
||||||
return ret.Value;
|
return ret.Value;
|
||||||
|
|
|
@ -199,10 +199,16 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(
|
||||||
}
|
}
|
||||||
sampleCount.value = bytes.length;
|
sampleCount.value = bytes.length;
|
||||||
|
|
||||||
|
const aDFrequency = resp.adFrequency;
|
||||||
|
|
||||||
|
// 计算采样周期(ns)
|
||||||
|
const samplePeriodNs =
|
||||||
|
aDFrequency > 0 ? 1_000_000_000 / aDFrequency : 200;
|
||||||
|
|
||||||
// 构建时间轴
|
// 构建时间轴
|
||||||
const x = Array.from(
|
const x = Array.from(
|
||||||
{ length: bytes.length },
|
{ length: bytes.length },
|
||||||
(_, i) => (i * samplePeriodNs.value) / 1000, // us
|
(_, i) => (i * samplePeriodNs) / 1000, // us
|
||||||
);
|
);
|
||||||
const y = Array.from(bytes);
|
const y = Array.from(bytes);
|
||||||
|
|
||||||
|
@ -211,11 +217,13 @@ const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(
|
||||||
y,
|
y,
|
||||||
xUnit: "us",
|
xUnit: "us",
|
||||||
yUnit: "V",
|
yUnit: "V",
|
||||||
adFrequency: resp.aDFrequency,
|
adFrequency: aDFrequency,
|
||||||
adVpp: resp.aDVpp,
|
adVpp: resp.adVpp,
|
||||||
adMax: resp.aDMax,
|
adMax: resp.adMax,
|
||||||
adMin: resp.aDMin,
|
adMin: resp.adMin,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("解析后的参数:", resp, oscData.value); // 添加调试日志
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
|
|
|
@ -88,6 +88,67 @@
|
||||||
<div class="w-2 h-2 bg-white rounded-full animate-pulse"></div>
|
<div class="w-2 h-2 bg-white rounded-full animate-pulse"></div>
|
||||||
采集中
|
采集中
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 测量数据展示面板 -->
|
||||||
|
<div
|
||||||
|
v-if="hasData"
|
||||||
|
class="absolute top-4 left-4 bg-white/95 dark:bg-slate-800/95 backdrop-blur-sm rounded-lg shadow-lg border border-slate-200/50 dark:border-slate-700/50 p-3 min-w-[200px]"
|
||||||
|
>
|
||||||
|
<h4 class="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3 flex items-center gap-2">
|
||||||
|
<Activity class="w-4 h-4 text-blue-500" />
|
||||||
|
测量参数
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<div class="space-y-2 text-xs">
|
||||||
|
<!-- 采样频率 -->
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">采样频率:</span>
|
||||||
|
<span class="font-mono font-semibold text-blue-600 dark:text-blue-400">
|
||||||
|
{{ formatFrequency(oscData?.adFrequency || 0) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 电压范围 -->
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">Vpp:</span>
|
||||||
|
<span class="font-mono font-semibold text-emerald-600 dark:text-emerald-400">
|
||||||
|
{{ (oscData?.adVpp || 0).toFixed(2) }}V
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 最大值 -->
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">最大值:</span>
|
||||||
|
<span class="font-mono font-semibold text-orange-600 dark:text-orange-400">
|
||||||
|
{{ formatAdcValue(oscData?.adMax || 0) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 最小值 -->
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">最小值:</span>
|
||||||
|
<span class="font-mono font-semibold text-purple-600 dark:text-purple-400">
|
||||||
|
{{ formatAdcValue(oscData?.adMin || 0) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 采样点数 -->
|
||||||
|
<div class="flex justify-between items-center pt-1 border-t border-slate-200 dark:border-slate-700">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">采样点:</span>
|
||||||
|
<span class="font-mono font-semibold text-slate-700 dark:text-slate-300">
|
||||||
|
{{ formatSampleCount(oscManager.sampleCount.value) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 采样周期 -->
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">周期:</span>
|
||||||
|
<span class="font-mono font-semibold text-slate-700 dark:text-slate-300">
|
||||||
|
{{ formatPeriod(oscManager.samplePeriodNs.value) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -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 => {
|
const option = computed((): EChartsOption => {
|
||||||
if (!oscData.value || !oscData.value.x || !oscData.value.y) {
|
if (!oscData.value || !oscData.value.x || !oscData.value.y) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -235,7 +334,9 @@ const option = computed((): EChartsOption => {
|
||||||
if (!oscData.value) return "";
|
if (!oscData.value) return "";
|
||||||
let result = `<div style="font-weight: 600; margin-bottom: 4px;">时间: ${params[0].data[0].toFixed(2)} ${oscData.value.xUnit}</div>`;
|
let result = `<div style="font-weight: 600; margin-bottom: 4px;">时间: ${params[0].data[0].toFixed(2)} ${oscData.value.xUnit}</div>`;
|
||||||
params.forEach((param: any) => {
|
params.forEach((param: any) => {
|
||||||
result += `<div style="color: ${param.color};">● ${param.seriesName}: ${param.data[1].toFixed(3)} ${oscData.value?.yUnit ?? ""}</div>`;
|
const adcValue = param.data[1];
|
||||||
|
const voltage = ((adcValue / 255) * 3.3).toFixed(3);
|
||||||
|
result += `<div style="color: ${param.color};">● ${param.seriesName}: ${adcValue} (${voltage}V)</div>`;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
@ -333,7 +434,7 @@ const option = computed((): EChartsOption => {
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
name: oscData.value ? `电压 (${oscData.value.yUnit})` : "电压",
|
name: oscData.value ? `ADC值 (0-255)` : "ADC值",
|
||||||
nameLocation: "middle",
|
nameLocation: "middle",
|
||||||
nameGap: 50,
|
nameGap: 50,
|
||||||
nameTextStyle: {
|
nameTextStyle: {
|
||||||
|
@ -357,6 +458,9 @@ const option = computed((): EChartsOption => {
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: "#64748B",
|
color: "#64748B",
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
|
formatter: (value: number) => {
|
||||||
|
return `${value} (${((value / 255) * 3.3).toFixed(1)}V)`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -536,6 +640,14 @@ button:active::after {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 4px 8px;
|
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: 2px solid rgba(59, 130, 246, 0.5);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
/* 测量面板样式增强 */
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -176,7 +176,10 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRotaryEncoder } from "@/stores/Peripherals/RotaryEncoder";
|
import { useRotaryEncoder } from "@/stores/Peripherals/RotaryEncoder";
|
||||||
import { RotaryEncoderDirection } from "@/utils/signalR/Peripherals.RotaryEncoderClient";
|
import {
|
||||||
|
RotaryEncoderDirection,
|
||||||
|
RotaryEncoderPressStatus,
|
||||||
|
} from "@/utils/signalR/Peripherals.RotaryEncoderClient";
|
||||||
import { watch } from "vue";
|
import { watch } from "vue";
|
||||||
import { watchEffect } from "vue";
|
import { watchEffect } from "vue";
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
|
@ -185,6 +188,7 @@ const rotataryEncoderStore = useRotaryEncoder();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: number;
|
size?: number;
|
||||||
|
componentId?: string;
|
||||||
enableDigitalTwin?: boolean;
|
enableDigitalTwin?: boolean;
|
||||||
encoderNumber?: number;
|
encoderNumber?: number;
|
||||||
}
|
}
|
||||||
|
@ -236,8 +240,16 @@ function handleMouseUp() {
|
||||||
// 仅在未发生旋转时才触发按压
|
// 仅在未发生旋转时才触发按压
|
||||||
if (!drag.value.hasRotated) {
|
if (!drag.value.hasRotated) {
|
||||||
isPressed.value = true;
|
isPressed.value = true;
|
||||||
|
rotataryEncoderStore.pressOnce(
|
||||||
|
props.encoderNumber,
|
||||||
|
RotaryEncoderPressStatus.Press,
|
||||||
|
);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isPressed.value = false;
|
isPressed.value = false;
|
||||||
|
rotataryEncoderStore.pressOnce(
|
||||||
|
props.encoderNumber,
|
||||||
|
RotaryEncoderPressStatus.Release,
|
||||||
|
);
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +264,10 @@ function handlePress(pressed: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
rotataryEncoderStore.setEnable(props.enableDigitalTwin);
|
if (!props.enableDigitalTwin) return;
|
||||||
|
|
||||||
|
if (props.componentId)
|
||||||
|
rotataryEncoderStore.setEnable(props.enableDigitalTwin);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|
|
@ -108,6 +108,7 @@ import { ref, computed, watch, onMounted } from "vue";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: number;
|
size?: number;
|
||||||
|
componentId?: string;
|
||||||
enableDigitalTwin?: boolean;
|
enableDigitalTwin?: boolean;
|
||||||
switchCount?: number;
|
switchCount?: number;
|
||||||
initialValues?: string;
|
initialValues?: string;
|
||||||
|
@ -191,13 +192,10 @@ function setBtnStatus(idx: number, isOn: boolean) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听 props 变化只同步一次
|
// 监听 props 变化只同步一次
|
||||||
const isFirstEnableDigitalTwin = ref(true);
|
|
||||||
watch(
|
watch(
|
||||||
() => props.enableDigitalTwin,
|
() => props.enableDigitalTwin,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (isFirstEnableDigitalTwin.value) {
|
if (props.componentId) {
|
||||||
isFirstEnableDigitalTwin.value = false;
|
|
||||||
} else {
|
|
||||||
const client = getClient();
|
const client = getClient();
|
||||||
client.setEnable(newVal);
|
client.setEnable(newVal);
|
||||||
}
|
}
|
||||||
|
@ -205,16 +203,11 @@ watch(
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
const isFirstUpdateStatus = ref(true);
|
|
||||||
watch(
|
watch(
|
||||||
() => [switchCount.value, props.initialValues],
|
() => [switchCount.value, props.initialValues],
|
||||||
() => {
|
() => {
|
||||||
btnStatus.value = parseInitialValues();
|
btnStatus.value = parseInitialValues();
|
||||||
if (isFirstUpdateStatus.value) {
|
if (props.componentId) updateStatus(btnStatus.value);
|
||||||
isFirstUpdateStatus.value = false;
|
|
||||||
} else {
|
|
||||||
updateStatus(btnStatus.value);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { AuthManager } from "@/utils/AuthManager";
|
import { AuthManager } from "@/utils/AuthManager";
|
||||||
import type { RotaryEncoderDirection } from "@/utils/signalR/Peripherals.RotaryEncoderClient";
|
import type {
|
||||||
|
RotaryEncoderDirection,
|
||||||
|
RotaryEncoderPressStatus,
|
||||||
|
} from "@/utils/signalR/Peripherals.RotaryEncoderClient";
|
||||||
import {
|
import {
|
||||||
getHubProxyFactory,
|
getHubProxyFactory,
|
||||||
getReceiverRegister,
|
getReceiverRegister,
|
||||||
|
@ -72,6 +75,11 @@ export const useRotaryEncoder = defineStore("RotaryEncoder", () => {
|
||||||
return await proxy.rotateEncoderOnce(num, direction);
|
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(
|
async function enableCycleRotate(
|
||||||
num: number,
|
num: number,
|
||||||
direction: RotaryEncoderDirection,
|
direction: RotaryEncoderDirection,
|
||||||
|
@ -89,6 +97,7 @@ export const useRotaryEncoder = defineStore("RotaryEncoder", () => {
|
||||||
return {
|
return {
|
||||||
setEnable,
|
setEnable,
|
||||||
rotateOnce,
|
rotateOnce,
|
||||||
|
pressOnce,
|
||||||
enableCycleRotate,
|
enableCycleRotate,
|
||||||
disableCycleRotate,
|
disableCycleRotate,
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,3 +8,9 @@ export enum RotaryEncoderDirection {
|
||||||
Clockwise = 1,
|
Clockwise = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderPressStatus */
|
||||||
|
export enum RotaryEncoderPressStatus {
|
||||||
|
Press = 0,
|
||||||
|
Release = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import type { HubConnection, IStreamResult, Subject } from '@microsoft/signalr';
|
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 { 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 { 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';
|
import type { RGBColor } from '../Peripherals.WS2812Client';
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,6 +270,10 @@ class IRotaryEncoderHub_HubProxy implements IRotaryEncoderHub {
|
||||||
return await this.connection.invoke("RotateEncoderOnce", num, direction);
|
return await this.connection.invoke("RotateEncoderOnce", num, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly pressEncoderOnce = async (num: number, press: RotaryEncoderPressStatus): Promise<boolean> => {
|
||||||
|
return await this.connection.invoke("PressEncoderOnce", num, press);
|
||||||
|
}
|
||||||
|
|
||||||
public readonly enableCycleRotateEncoder = async (num: number, direction: RotaryEncoderDirection, freq: number): Promise<boolean> => {
|
public readonly enableCycleRotateEncoder = async (num: number, direction: RotaryEncoderDirection, freq: number): Promise<boolean> => {
|
||||||
return await this.connection.invoke("EnableCycleRotateEncoder", num, direction, freq);
|
return await this.connection.invoke("EnableCycleRotateEncoder", num, direction, freq);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import type { IStreamResult, Subject } from '@microsoft/signalr';
|
import type { IStreamResult, Subject } from '@microsoft/signalr';
|
||||||
import type { DigitalTubeTaskStatus, OscilloscopeFullConfig, OscilloscopeDataResponse, ProgressInfo } 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';
|
import type { RGBColor } from '../Peripherals.WS2812Client';
|
||||||
|
|
||||||
export type IDigitalTubesHub = {
|
export type IDigitalTubesHub = {
|
||||||
|
@ -116,6 +116,12 @@ export type IRotaryEncoderHub = {
|
||||||
rotateEncoderOnce(num: number, direction: RotaryEncoderDirection): Promise<boolean>;
|
rotateEncoderOnce(num: number, direction: RotaryEncoderDirection): Promise<boolean>;
|
||||||
/**
|
/**
|
||||||
* @param num Transpiled from int
|
* @param num Transpiled from int
|
||||||
|
* @param press Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderPressStatus
|
||||||
|
* @returns Transpiled from System.Threading.Tasks.Task<bool>
|
||||||
|
*/
|
||||||
|
pressEncoderOnce(num: number, press: RotaryEncoderPressStatus): Promise<boolean>;
|
||||||
|
/**
|
||||||
|
* @param num Transpiled from int
|
||||||
* @param direction Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderDirection
|
* @param direction Transpiled from Peripherals.RotaryEncoderClient.RotaryEncoderDirection
|
||||||
* @param freq Transpiled from int
|
* @param freq Transpiled from int
|
||||||
* @returns Transpiled from System.Threading.Tasks.Task<bool>
|
* @returns Transpiled from System.Threading.Tasks.Task<bool>
|
||||||
|
|
|
@ -13,13 +13,13 @@ export type DigitalTubeTaskStatus = {
|
||||||
/** Transpiled from server.Hubs.OscilloscopeDataResponse */
|
/** Transpiled from server.Hubs.OscilloscopeDataResponse */
|
||||||
export type OscilloscopeDataResponse = {
|
export type OscilloscopeDataResponse = {
|
||||||
/** Transpiled from uint */
|
/** Transpiled from uint */
|
||||||
aDFrequency: number;
|
adFrequency: number;
|
||||||
/** Transpiled from byte */
|
/** Transpiled from byte */
|
||||||
aDVpp: number;
|
adVpp: number;
|
||||||
/** Transpiled from byte */
|
/** Transpiled from byte */
|
||||||
aDMax: number;
|
adMax: number;
|
||||||
/** Transpiled from byte */
|
/** Transpiled from byte */
|
||||||
aDMin: number;
|
adMin: number;
|
||||||
/** Transpiled from string */
|
/** Transpiled from string */
|
||||||
waveformData: string;
|
waveformData: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,7 @@
|
||||||
<!-- MD文档 -->
|
<!-- MD文档 -->
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label class="text-sm font-medium text-base-content"
|
<label class="text-sm font-medium text-base-content"
|
||||||
>MD文档 (必需)</label
|
>MD文档 (可选)</label
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="border-2 border-dashed border-base-300 rounded-lg p-6 text-center cursor-pointer hover:border-primary hover:bg-primary/5 transition-colors aspect-square flex items-center justify-center"
|
class="border-2 border-dashed border-base-300 rounded-lg p-6 text-center cursor-pointer hover:border-primary hover:bg-primary/5 transition-colors aspect-square flex items-center justify-center"
|
||||||
|
@ -468,7 +468,7 @@ const canCreateExam = computed(() => {
|
||||||
editExamInfo.value.id.trim() !== "" &&
|
editExamInfo.value.id.trim() !== "" &&
|
||||||
editExamInfo.value.name.trim() !== "" &&
|
editExamInfo.value.name.trim() !== "" &&
|
||||||
editExamInfo.value.description.trim() !== "" &&
|
editExamInfo.value.description.trim() !== "" &&
|
||||||
(uploadFiles.value.mdFile !== null || mode.value === "edit")
|
(mode.value === "edit")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -605,11 +605,6 @@ const submitCreateExam = async () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!uploadFiles.value.mdFile) {
|
|
||||||
alert.error("请上传MD文档");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUpdating.value = true;
|
isUpdating.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in New Issue