Compare commits

...
18 Commits
Author SHA1 Message Date
“hsc” ac26ab255c 处理掉多余文件,修改CAN描述 2026-09-14 09:40:02 +08:00
“hsc” 60d788178f P3: 数据库文件膨胀 → 启动时检查文件过期(默认180天),过期则归档重命名(SQL_归档_yyyyMMdd_HHmmss.db),不删除记录 2026-09-10 15:01:09 +08:00
“hsc” fa76bdb61c P3: DeviceHealthMonitor 更新 DeviceInfoVM.IsConnected 改用 Dispatcher.BeginInvoke 切回UI线程 2026-09-10 14:33:40 +08:00
“hsc” e7702405d8 P2: Flexible 四类通信锁从全局静态改为按端口/IP粒度(ConcurrentDictionary),不同物理端口互不阻塞 2026-09-10 14:32:22 +08:00
“hsc” 2ddaf0e6f7 P2: DeviceHealthMonitor 重连增加指数退避策略(3→5→7→...→15) 2026-09-10 14:27:39 +08:00
“hsc” bd1c25a67d P1: DeviceHealthMonitor 增加 TCP 设备主动探活(*IDN? 2s超时)检测死连接 2026-09-10 14:25:40 +08:00
“hsc” 9c7c426507 P1: HardwareDataBroadcaster 采样容错增加 Warn 日志与连续失败暂停报警 2026-09-10 14:23:30 +08:00
“hsc” 11fe48ea9a P0: GlobalInfo 四个 Dictionary 改为 ConcurrentDictionary 2026-09-10 14:20:55 +08:00
“hsc” 18995ee0e2 ANEVH设备类修正 2026-09-10 14:02:10 +08:00
“hsc” d83843d2fd list模式scpi命令添加 2026-09-10 14:01:58 +08:00
“hsc” d88b81a8f0 软件独立控制弹窗添加 2026-09-10 14:01:50 +08:00
“hsc” d35e451c40 设备加载页 2026-08-31 15:30:53 +08:00
“hsc” 3b7a41d7f8 测试报告导出修改 2026-08-30 15:57:28 +08:00
“hsc” e8a7aa52aa 弹窗添加 2026-08-30 15:40:14 +08:00
“hsc” c41190aa33 样式优化 2026-08-27 13:37:06 +08:00
“hsc” f46df16275 水冷机驱动注释优化 2026-08-27 11:17:38 +08:00
“hsc” eabe73edd5 合并水冷机驱动冲突 2026-08-27 11:09:35 +08:00
“hsc” e144dc78a7 命令树优化,测试编辑区域优化 2026-08-27 10:50:10 +08:00
207 changed files with 5716 additions and 731237 deletions
+57
View File
@@ -1,4 +1,5 @@
using Common; using Common;
using Command;
using ACP.ViewModels; using ACP.ViewModels;
using ACP.ViewModels.Dialogs; using ACP.ViewModels.Dialogs;
using ACP.Views; using ACP.Views;
@@ -12,6 +13,8 @@ using Service.Interface;
using System.Configuration; using System.Configuration;
using System.Data; using System.Data;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using UIShare.PubEvent; using UIShare.PubEvent;
using static System.Runtime.InteropServices.JavaScript.JSType; using static System.Runtime.InteropServices.JavaScript.JSType;
@@ -23,6 +26,7 @@ using DeviceCommand.Base;
using AutoMapper; using AutoMapper;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using ACP.Profiles; using ACP.Profiles;
using Prism.Dialogs;
using UIShare.Helpers; using UIShare.Helpers;
namespace ACP namespace ACP
@@ -60,6 +64,56 @@ namespace ACP
} }
protected override void OnInitialized() protected override void OnInitialized()
{ {
// 向命令库 CommandDialog 注入弹窗能力(命令库是纯 .NET 类库,不引用 WPF,这里通过委托实现依赖倒置)
var dialogService = Container.Resolve<IDialogService>();
CommandDialog. = (, , , , ct) =>
{
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
CancellationTokenRegistration registration = default;
if ()
{
registration = ct.Register(() => tcs.TrySetCanceled(ct));
}
// 命令可能在后台线程执行,统一切回 UI 线程弹窗
Application.Current.Dispatcher.Invoke(() =>
{
var param = new DialogParameters
{
{ "Title", switch
{
CommandDialog.DialogType.Warning => "警告",
CommandDialog.DialogType.Error => "错误",
_ => "信息提示"
} },
{ "Message", ?? string.Empty },
{ "Icon", switch
{
CommandDialog.DialogType.Warning => "warn",
CommandDialog.DialogType.Error => "error",
_ => "info"
} },
{ "ShowOk", true },
{ "AutoCloseSeconds", (double) }
};
if ()
{
// 阻塞:用户手动关闭或自动关闭后才继续执行步骤
dialogService.ShowDialog("MessageBox", param, _ =>
{
registration.Dispose();
tcs.TrySetResult(true);
});
}
else
{
// 非阻塞:弹出后步骤立即继续(不等关闭回调)
dialogService.Show("MessageBox", param, _ => { });
tcs.TrySetResult(true);
}
});
return tcs.Task;
};
// 配置全局日志分发器:按 CurrentScope 路由到对应 LogArea // 配置全局日志分发器:按 CurrentScope 路由到对应 LogArea
var globalInfo = Container.Resolve<GlobalInfo>(); var globalInfo = Container.Resolve<GlobalInfo>();
LoggerHelper.Progress = new ScopeLogDispatcher(globalInfo); LoggerHelper.Progress = new ScopeLogDispatcher(globalInfo);
@@ -67,6 +121,8 @@ namespace ACP
//初始化数据库 //初始化数据库
DatabaseConfig.SetTenant(10001); DatabaseConfig.SetTenant(10001);
DatabaseConfig.InitSqlite(); DatabaseConfig.InitSqlite();
// 启动时检查数据库文件是否过期,过期则归档(重命名加时间戳),后续会自动创建新库
DatabaseConfig.TryArchiveOldDatabase();
DatabaseConfig.CreateDatabaseAndCheckConnection(createDatabase: true, checkConnection: true); DatabaseConfig.CreateDatabaseAndCheckConnection(createDatabase: true, checkConnection: true);
SqlSugarContext.InitDatabase(); SqlSugarContext.InitDatabase();
//显示登录窗口 //显示登录窗口
@@ -126,6 +182,7 @@ namespace ACP
//注册服务 //注册服务
containerRegistry.Register<IMonitorValueService, MonitorValueService>(); containerRegistry.Register<IMonitorValueService, MonitorValueService>();
containerRegistry.Register<ITestReportService, TestReportService>(); containerRegistry.Register<ITestReportService, TestReportService>();
containerRegistry.Register<ITestCheckRecordService, TestCheckRecordService>();
} }
//指定模块加载方式(需要手动将模块生成的dll放入Modules文件夹中) //指定模块加载方式(需要手动将模块生成的dll放入Modules文件夹中)
protected override IModuleCatalog CreateModuleCatalog() protected override IModuleCatalog CreateModuleCatalog()
@@ -1,7 +1,9 @@
using UIShare.PubEvent; using UIShare.PubEvent;
using UIShare.ViewModelBase; using UIShare.ViewModelBase;
using System;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
namespace ACP.ViewModels.Dialogs namespace ACP.ViewModels.Dialogs
{ {
@@ -69,6 +71,11 @@ namespace ACP.ViewModels.Dialogs
public DialogCloseListener RequestClose { get; set; } public DialogCloseListener RequestClose { get; set; }
/// <summary>
/// 自动关闭定时器:自动关闭秒数大于 0 时启动,到时自动关闭弹窗(同时解除阻塞等待)
/// </summary>
private DispatcherTimer _autoCloseTimer;
public MessageBoxViewModel(IContainerProvider containerProvider):base(containerProvider) public MessageBoxViewModel(IContainerProvider containerProvider):base(containerProvider)
{ {
YesCommand = new DelegateCommand(OnYes); YesCommand = new DelegateCommand(OnYes);
@@ -93,6 +100,7 @@ namespace ACP.ViewModels.Dialogs
public override void OnDialogClosed() public override void OnDialogClosed()
{ {
_autoCloseTimer?.Stop();
_eventAggregator.GetEvent<OverlayEvent>().Publish(false); _eventAggregator.GetEvent<OverlayEvent>().Publish(false);
} }
@@ -117,6 +125,21 @@ namespace ACP.ViewModels.Dialogs
ShowNo = parameters.GetValue<bool>("ShowNo"); ShowNo = parameters.GetValue<bool>("ShowNo");
ShowOk = parameters.GetValue<bool>("ShowOk"); ShowOk = parameters.GetValue<bool>("ShowOk");
ShowCancel = parameters.GetValue<bool>("ShowCancel"); ShowCancel = parameters.GetValue<bool>("ShowCancel");
// 自动关闭:秒数大于 0 时启动定时器,到时自动关闭(供命令库 弹窗 命令的自动关闭参数使用)
if (parameters.TryGetValue("AutoCloseSeconds", out double autoCloseSeconds) && autoCloseSeconds > 0)
{
_autoCloseTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(autoCloseSeconds)
};
_autoCloseTimer.Tick += (s, e) =>
{
_autoCloseTimer.Stop();
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
};
_autoCloseTimer.Start();
}
} }
#endregion #endregion
} }
+1 -1
View File
@@ -227,7 +227,7 @@ namespace ACP.ViewModels
SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer); SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer);
SettingChannelCommand = new DelegateCommand(SettingChannel); SettingChannelCommand = new DelegateCommand(SettingChannel);
GoBackCommand = new DelegateCommand(OnGoBack); GoBackCommand = new DelegateCommand(OnGoBack);
_globalInfo.ContextDic.Add("default", new ScopedContext()); _globalInfo.ContextDic.TryAdd("default", new ScopedContext());
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() => _eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
{ {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

-47
View File
@@ -1,47 +0,0 @@
===== Sheet: Sheet1 (45行x16列) =====
集成化测试系统:ACP交流源适应性系统控制接线图(三工位系统)
注:以下操作是在执行就地系统合上断路器(QF1、)后按下SB1启动按钮后仪器设备得电后才能操作。
序号 | 对应设备 | | IO输入口 | 控制对应中间继电器 | 对应输出元件通断/吸合 | 功能 | | | | 备注
1 | 1#产品 | IO继电器模块 1# | Y1 | KA2-1 | KM2 | Y1输出控制KA2-1执行,KM2吸合,产品充电(三相) | | | | 对应控制1# (低压直流电源)/电子锁/温度检测/CC/CP信号。
2 | | | Y2 | KA3-1 | KM3 | Y2输出控制KA3-1执行,KM3吸合,产品放电(三相)
| | | Y3 | KA4-1 | KL30通断 | Y3输出控制KA4-1执行KL30通断
| | | Y4 | KA5-1 | KL15通断 | Y4输出控制KA5-1执行KL15通断
| | | Y5 | KA6-1 | 电子锁60Ω电阻 | Y5输出控制KA6-1执行电子锁信号通断(60Ω)
| | | Y6 | KA7-1 | 温度检测1KΩ电阻 | Y6输出控制KA7-1执行温度检测信号通断(1KΩ)
3 | | | Y7 | KA8-1 | CC信号串1KΩ电阻 | Y7输出控制KA8-1执行CC信号通断(1KΩ)
4 | | | Y8 | KA9-1 | CP信号串1KΩ电阻 | Y8输出控制KA9-1执行CP信号通断(1KΩ)
5 | | | Y9 | KA10-1 | CC信号串680Ω电阻 | Y9输出控制KA10-1执行CC信号通断(680Ω)
6 | | | Y10 | KA11-1 | CC信号串220Ω电阻 | Y10输出控制KA11-1执行CC信号通断(220Ω)
7 | | | Y11 | KA12-1 | CC信号串100Ω电阻 | Y11输出控制KA12-1执行CC信号通断(100Ω)
8 | | | Y12 | KA13-1 | CC信号空载 | Y12输出控制KA13-1执行CC信号通断(100Ω)
13 | | | Y13 | KA14-1 | 蜂鸣器 1#响应 | Y13输出控制KA14-1执行蜂鸣器1#信号通断
15 | 2#产品 | IO继电器模块 2# | Y1 | KA2-2 | KM4 | Y2输出控制KA2-2执行,KM4吸合,产品充电(三相) | | | | 对应控制2# (低压直流电源)/电子锁/温度检测/CC/CP信号。
16 | | | Y2 | KA3-2 | KM5 | Y2输出控制KA3-2执行,KM5吸合,产品放电(三相)
| | | Y3 | KA4-2 | KL30通断 | Y3输出控制KA4-2执行KL30通断
| | | Y4 | KA5-2 | KL15通断 | Y4输出控制KA5-2执行KL15通断
17 | | | Y5 | KA6-2 | 电子锁60Ω电阻 | Y5输出控制KA6-2执行电子锁信号通断(60Ω)
18 | | | Y6 | KA7-2 | 温度检测1KΩ电阻 | Y6输出控制KA7-2执行温度检测信号通断(1KΩ)
19 | | | Y7 | KA8-2 | CC信号串1KΩ电阻 | Y7输出控制KA8-2执行CC信号通断(1KΩ)
20 | | | Y8 | KA9-2 | CP信号串1KΩ电阻 | Y8输出控制KA9-2执行CP信号通断(1KΩ)
21 | | | Y9 | KA10-2 | CC信号串680Ω电阻 | Y9输出控制KA10-2执行CC信号通断(680Ω)
22 | | | Y10 | KA11-2 | CC信号串220Ω电阻 | Y10输出控制KA11-2执行CC信号通断(220Ω)
23 | | | Y11 | KA12-2 | CC信号串100Ω电阻 | Y11输出控制KA12-2执行CC信号通断(100Ω)
24 | | | Y12 | KA13-2 | CC信号空载 | Y12输出控制KA13-2执行CC信号通断(100Ω)
25 | | | Y13 | KA14-2 | 蜂鸣器 2#响应 | Y13输出控制KA14-2执行蜂鸣器2#信号通断
28 | 3#产品 | IO继电器模块 3# | Y1 | KA2-3 | KM6 | Y1输出控制KA2-3执行,KM6吸合,产品充电(三相) | | | | 对应控制3# (低压直流电源)/电子锁/温度检测/CC/CP信号。
29 | | | Y2 | KA3-3 | KM7 | Y2输出控制KA3-3执行,KM7吸合,产品放电(三相)
30 | | | Y3 | KA4-3 | KL30通断 | Y3输出控制KA4-3执行KL30通断
31 | | | Y4 | KA5-3 | KL15通断 | Y4输出控制KA5-3执行KL15通断
32 | | | Y5 | KA6-3 | 电子锁60Ω电阻 | Y5输出控制KA6-3执行电子锁信号通断(60Ω)
33 | | | Y6 | KA7-3 | 温度检测1KΩ电阻 | Y6输出控制KA7-3执行温度检测信号通断(1KΩ)
34 | | | Y7 | KA8-3 | CC信号串1KΩ电阻 | Y7输出控制KA8-3执行CC信号通断(1KΩ)
35 | | | Y8 | KA9-3 | CP信号串1KΩ电阻 | Y8输出控制KA9-3执行CP信号通断(1KΩ)
36 | | | Y9 | KA10-3 | CC信号串680Ω电阻 | Y9输出控制KA10-3执行CC信号通断(680Ω)
37 | | | Y10 | KA11-3 | CC信号串220Ω电阻 | Y10输出控制KA11-3执行CC信号通断(220Ω)
38 | | | Y11 | KA12-3 | CC信号串100Ω电阻 | Y11输出控制KA12-3执行CC信号通断(100Ω)
39 | | | Y12 | KA13-3 | CC信号空载 | Y12输出控制KA13-3执行CC信号通断(100Ω)
40 | | | Y13 | KA14-3 | 蜂鸣器 3#响应 | Y13输出控制KA14-3执行蜂鸣器2#信号通断
41 | | | Y14 | KA15-3 | KM8 | Y14输出控制KA15-3执行KM8抛负载通断 | | | | HV
42 | | | Y15 | KA16-3 | KM9 | Y15输出控制KA16-3执行KM9短路测试
43 | | | Y16 | KA17-3 | KM10 | Y16输出控制KA17-3执行KM10短路测试 | | | | LV
-54
View File
@@ -1,54 +0,0 @@
# -*- coding: utf-8 -*-
"""分析 D:\ACP 下所有 .ACP 测试项/子程序的结构。"""
import json, os, glob
def walk_steps(steps, depth, out):
for s in steps:
st = s.get("StepType")
name = s.get("Name")
ind = " " * depth
if st == "方法":
m = s.get("Method") or {}
full = m.get("FullName", "?")
cls = full.split(".")[-1]
params = []
for p in (m.get("Parameters") or []):
if p.get("Category") == 0: # Input
v = p.get("Value")
if p.get("IsUseVar"):
v = f"${p.get('VariableName')}"
params.append(f"{p.get('Name')}={v}")
out.append(f"{ind}[方法] {name} <- {cls}({', '.join(params)})")
elif st == "子程序":
out.append(f"{ind}[子程序] {name}")
sp = s.get("SubProgram")
if sp and sp.get("StepCollection"):
walk_steps(sp["StepCollection"], depth + 1, out)
elif st == "循环开始":
lc = s.get("LoopCount")
out.append(f"{ind}[循环开始] 次数={lc}")
elif st == "循环结束":
out.append(f"{ind}[循环结束]")
elif st == "跳转":
out.append(f"{ind}[跳转] {name} expr={s.get('OKExpression')}")
else:
out.append(f"{ind}[{st}] {name}")
dirs = [r"D:\ACP\测试项", r"D:\ACP\子程序"]
for d in dirs:
for fp in sorted(glob.glob(os.path.join(d, "*.ACP"))):
print("=" * 70)
print("FILE:", os.path.basename(fp))
with open(fp, encoding="utf-8") as f:
data = json.load(f)
out = []
walk_steps(data.get("StepCollection") or [], 0, out)
# 只打印前 60 行,避免过长
for line in out[:60]:
print(line)
if len(out) > 60:
print(f"... (共 {len(out)} 步,已省略)")
# 参数
print(" -- Parameters --")
for p in (data.get("Parameters") or []):
print(f" {p.get('Name')} = {p.get('Value')}")
-215
View File
@@ -1,215 +0,0 @@
# -*- coding: utf-8 -*-
"""
极电子程序测试项转换脚本
将极电子程序的 .ats 文件转换为我的项目的 .ats 文件
设备映射关系:
- EA3040_40C → ANEVH80(水冷机,占位符)
- IT6724C → IT6724C(直流源)
- PSB11500_60 → PSB11500_60(交流源/负载)
- SQ0090G1D1 → SQ0090G1D1(三相交流源)
- IOBoardCard → IOBoardGroup(占位符,只传台架序号)
- TSMasterCAN.CAN → TSMasterCAN.CAN(相同)
"""
import json
import os
import uuid
import re
from pathlib import Path
# 输入输出路径
INPUT_DIR = Path(r"C:\Users\kk\Desktop\ACP\极电子程序")
OUTPUT_DIR = Path(r"D:\ACP\测试项\极电子对应")
# 设备映射
DEVICE_MAPPING = {
"DeviceCommand.Device.EA3040_40C": "DeviceCommand.Device.ANEVH80", # 水冷机(占位符)
"DeviceCommand.Device.IT6724C": "DeviceCommand.Device.IT6724C",
"DeviceCommand.Device.PSB11500_60": "DeviceCommand.Device.PSB11500_60",
"DeviceCommand.Device.SQ0090G1D1": "DeviceCommand.Device.SQ0090G1D1",
"DeviceCommand.Device.IOBoardCard": "DeviceCommand.Device.IOBoardGroup", # IO板卡(占位符)
"TSMasterCAN.CAN": "TSMasterCAN.CAN",
}
# 方法映射(ANEVH80 占位符方法)
ANVH80_METHOD_MAPPING = {
"Set_RemoteMode": "Placeholder_SetRemoteMode",
"Set_Voltage": "Placeholder_SetVoltage",
"Set_Current": "Placeholder_SetCurrent",
"Set_Power": "Placeholder_SetPower",
"ON": "Placeholder_ON",
"OFF": "Placeholder_OFF",
}
# IOBoardGroup 占位符方法
IOBOARDGROUP_METHOD_MAPPING = {
"WriteMultiIO": "Placeholder_WriteMultiIO",
"WriteSingleIO": "Placeholder_WriteSingleIO",
"ReadMultiIO": "Placeholder_ReadMultiIO",
"ReadSingleIO": "Placeholder_ReadSingleIO",
}
def generate_guid():
"""生成新的 GUID"""
return str(uuid.uuid4())
def transform_parameter(param, add_station_var=False):
"""转换参数格式"""
new_param = {
"ID": generate_guid(),
"IsVisible": param.get("IsVisible", True),
"Name": param.get("Name", ""),
"Type": param.get("Type", ""),
"Category": param.get("Category", 0),
"IsGlobal": param.get("IsGlobal", False),
"InitialValue": param.get("InitialValue"),
"Value": param.get("Value"),
"LowerLimit": param.get("LowerLimit"),
"UpperLimit": param.get("UpperLimit"),
"Result": param.get("Result", True),
"IsUseVar": param.get("IsUseVar", False),
"IsSave": param.get("IsSave", False),
"VariableName": param.get("VariableName"),
"VariableID": param.get("VariableID"),
"IsOutputToReport": param.get("IsOutputToReport", False),
}
return new_param
def transform_method(method, step_name=""):
"""转换方法"""
if method is None:
return None
full_name = method.get("FullName", "")
method_name = method.get("Name", "")
# 设备映射
new_full_name = DEVICE_MAPPING.get(full_name, full_name) if full_name else None
# 方法映射(占位符)
if new_full_name and "ANEVH80" in new_full_name:
new_method_name = ANVH80_METHOD_MAPPING.get(method_name, method_name)
elif new_full_name and "IOBoardGroup" in new_full_name:
new_method_name = IOBOARDGROUP_METHOD_MAPPING.get(method_name, method_name)
else:
new_method_name = method_name
# 转换参数
new_params = []
for param in method.get("Parameters", []):
new_param = transform_parameter(param)
new_params.append(new_param)
new_method = {
"Name": new_method_name,
"FullName": new_full_name,
"DeviceID": method.get("DeviceID"),
"Parameters": new_params,
}
return new_method
def transform_step(step):
"""转换步骤"""
new_step = {
"ID": generate_guid(),
"IsUsed": step.get("IsUsed", True),
"Index": step.get("Index", 1),
"Name": step.get("Name", ""),
"StepType": step.get("StepType", "方法"),
"Method": None,
"SubProgram": None,
"LoopCount": step.get("LoopCount"),
"LoopStartStepId": step.get("LoopStartStepId"),
"OKExpression": step.get("OKExpression"),
"GotoSettingString": step.get("GotoSettingString", ""),
"OKGotoStepID": step.get("OKGotoStepID"),
"NGGotoStepID": step.get("NGGotoStepID"),
"Description": step.get("Description"),
}
# 转换方法
if step.get("Method"):
new_step["Method"] = transform_method(step["Method"], step.get("Name", ""))
# 转换子程序
if step.get("SubProgram"):
sub_program = step["SubProgram"]
new_sub_steps = []
for sub_step in sub_program.get("StepCollection", []):
new_sub_step = transform_step(sub_step)
new_sub_steps.append(new_sub_step)
new_step["SubProgram"] = {
"ID": generate_guid(),
"StepCollection": new_sub_steps,
"Parameters": [transform_parameter(p) for p in sub_program.get("Parameters", [])],
"Devices": sub_program.get("Devices", []),
}
return new_step
def transform_program(program):
"""转换整个程序"""
new_steps = []
for step in program.get("StepCollection", []):
new_step = transform_step(step)
new_steps.append(new_step)
new_program = {
"ID": generate_guid(),
"StepCollection": new_steps,
"Parameters": [transform_parameter(p) for p in program.get("Parameters", [])],
"Devices": program.get("Devices", []),
}
return new_program
def process_file(input_file, output_file):
"""处理单个文件"""
print(f"处理: {input_file.name}")
# 读取文件(支持 UTF-8 BOM
with open(input_file, 'r', encoding='utf-8-sig') as f:
program = json.load(f)
# 转换
new_program = transform_program(program)
# 写入文件
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(new_program, f, ensure_ascii=False, indent=2)
print(f" -> 已生成: {output_file.name}")
def main():
"""主函数"""
# 创建输出目录
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# 获取所有 .ats 文件
ats_files = list(INPUT_DIR.glob("*.ats"))
print(f"找到 {len(ats_files)} 个 .ats 文件")
print(f"输出目录: {OUTPUT_DIR}")
print()
# 处理每个文件
for input_file in ats_files:
output_file = OUTPUT_DIR / input_file.name
process_file(input_file, output_file)
print()
print(f"完成!共处理 {len(ats_files)} 个文件")
if __name__ == "__main__":
main()
-38
View File
@@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
"""将 ACP.pdf 逐页渲染为 PNG,并把 Excel 接线图导出为文本。"""
import fitz
import openpyxl
import os
pdf_path = r"C:\Users\kk\Desktop\ACP\ACP电路图\ACP.pdf"
xlsx_path = r"C:\Users\kk\Desktop\ACP\ACP电路图\ACP硬件接线图.xlsx"
out_dir = r"C:\Users\kk\Desktop\ACP\ACP电路图\_pages"
os.makedirs(out_dir, exist_ok=True)
# ---- 渲染 PDF ----
doc = fitz.open(pdf_path)
mat = fitz.Matrix(2.0, 2.0) # 2倍缩放,保证清晰度
for i, page in enumerate(doc):
pix = page.get_pixmap(matrix=mat)
png_path = os.path.join(out_dir, f"page_{i+1:02d}.png")
pix.save(png_path)
print(f"saved {png_path}")
doc.close()
# ---- 导出 Excel ----
wb = openpyxl.load_workbook(xlsx_path, data_only=True)
txt_path = os.path.join(out_dir, "接线图.txt")
with open(txt_path, "w", encoding="utf-8") as f:
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
f.write(f"===== Sheet: {sheet_name} ({ws.max_row}行x{ws.max_column}列) =====\n")
for row in ws.iter_rows(values_only=True):
cells = ["" if c is None else str(c).strip() for c in row]
# 去掉行尾空单元格
while cells and cells[-1] == "":
cells.pop()
if not cells:
continue
f.write(" | ".join(cells) + "\n")
f.write("\n")
print(f"saved {txt_path}")
-280
View File
@@ -1,280 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\AC侧电流检测.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_LOAD_MODE = "DeviceCommand.Devices.S7200负载模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def s7200_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.S7200", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_RATED_FREQ = guid() # 额定频率
VAR_25PCT_POWER = guid() # 25%负载功率
VAR_50PCT_POWER = guid() # 50%负载功率
VAR_75PCT_POWER = guid() # 75%负载功率
VAR_100PCT_POWER = guid() # 100%负载功率
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_STABLE_TIME = guid() # 稳定等待时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_25PCT_POWER, "IsVisible": True, "IsEditable": True, "Name": "25%负载功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_50PCT_POWER, "IsVisible": True, "IsEditable": True, "Name": "50%负载功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_75PCT_POWER, "IsVisible": True, "IsEditable": True, "Name": "75%负载功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_100PCT_POWER, "IsVisible": True, "IsEditable": True, "Name": "100%负载功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STABLE_TIME, "IsVisible": True, "IsEditable": True, "Name": "稳定等待时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源设置额定输入 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(9, "启动List输出(额定输入)", "启动List输出", []))
steps.append(delay_step(10, "等待OBC启动", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 三、负载(S7200)初始化 ----
steps.append(s7200_step(11, "设置为远程模式", "设置为远程模式", []))
steps.append(s7200_step(12, "设置负载工作模式CC", "设置负载工作模式",
[param("模式", ENUM_LOAD_MODE, 0)])) # CC=0
# ---- 四、25%负载点测试 ----
steps.append(s7200_step(13, "设置CC模式电流(25%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="25%负载功率", var_id=VAR_25PCT_POWER)]))
steps.append(s7200_step(14, "打开通道(25%负载)", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", True)]))
steps.append(delay_step(15, "等待25%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(16, "测量25%负载输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、50%负载点测试 ----
steps.append(s7200_step(17, "设置CC模式电流(50%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="50%负载功率", var_id=VAR_50PCT_POWER)]))
steps.append(delay_step(18, "等待50%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(19, "测量50%负载输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 六、75%负载点测试 ----
steps.append(s7200_step(20, "设置CC模式电流(75%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="75%负载功率", var_id=VAR_75PCT_POWER)]))
steps.append(delay_step(21, "等待75%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(22, "测量75%负载输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 七、100%负载点测试 ----
steps.append(s7200_step(23, "设置CC模式电流(100%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="100%负载功率", var_id=VAR_100PCT_POWER)]))
steps.append(delay_step(24, "等待100%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(25, "测量100%负载输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 八、收尾 ----
steps.append(s7200_step(26, "关闭负载通道", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", False)]))
steps.append(chroma_step(27, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(28, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(29, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\AC侧电流检测.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试负载点:")
print(" - 25% 额定负载")
print(" - 50% 额定负载")
print(" - 75% 额定负载")
print(" - 100% 额定负载")
-281
View File
@@ -1,281 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\AC侧电压检测.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_UPPER_VOLT = guid() # 输入电压上限
VAR_LOWER_VOLT = guid() # 输入电压下限
VAR_FREQ = guid() # 额定频率
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_HOLD_TIME = guid() # 各电压点保持时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_UPPER_VOLT, "IsVisible": True, "IsEditable": True, "Name": "输入电压上限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LOWER_VOLT, "IsVisible": True, "IsEditable": True, "Name": "输入电压下限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_HOLD_TIME, "IsVisible": True, "IsEditable": True, "Name": "各电压点保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源初始化 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(6, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
# ---- 三、额定电压测试点 ----
steps.append(chroma_step(7, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(9, "启动List输出(额定电压)", "启动List输出", []))
steps.append(delay_step(10, "等待额定电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(11, "测量额定电压点实际电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 四、上限电压测试点 ----
steps.append(chroma_step(12, "配置List交流起始电压(上限)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT)]))
steps.append(chroma_step(13, "配置List交流结束电压(上限)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT)]))
steps.append(delay_step(14, "等待上限电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(15, "测量上限电压点实际电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、下限电压测试点 ----
steps.append(chroma_step(16, "配置List交流起始电压(下限)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT)]))
steps.append(chroma_step(17, "配置List交流结束电压(下限)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT)]))
steps.append(delay_step(18, "等待下限电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(19, "测量下限电压点实际电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 六、恢复额定电压 ----
steps.append(chroma_step(20, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(21, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(delay_step(22, "等待额定电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 七、收尾 ----
steps.append(chroma_step(23, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(24, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(25, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\AC侧电压检测.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试点:")
print(" - 额定输入电压点")
print(" - 输入电压上限点")
print(" - 输入电压下限点")
-253
View File
@@ -1,253 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\输入频率范围测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量(Parameters 中 Category=0,供步骤绑定)
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_RATED_FREQ = guid() # 额定频率
VAR_UPPER_FREQ = guid() # 频率上限
VAR_LOWER_FREQ = guid() # 频率下限
VAR_HOLD_TIME = guid() # 各频率点保持时间(ms)
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_UPPER_FREQ, "IsVisible": True, "IsEditable": True, "Name": "频率上限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LOWER_FREQ, "IsVisible": True, "IsEditable": True, "Name": "频率下限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_HOLD_TIME, "IsVisible": True, "IsEditable": True, "Name": "各频率点保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、初始化交流源 ----
steps.append(chroma_step(1, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(2, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(3, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(4, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
# ---- 二、额定频率启动OBC ----
steps.append(chroma_step(5, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(6, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(7, "启动List输出(额定频率)", "启动List输出", []))
steps.append(delay_step(8, "等待OBC启动稳定", var_name="各频率点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 三、升频至上限 ----
steps.append(chroma_step(9, "升频至频率上限", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ)]))
steps.append(chroma_step(10, "保持频率上限", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="频率上限", var_id=VAR_UPPER_FREQ)]))
steps.append(delay_step(11, "频率上限保持时间", var_name="各频率点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 四、降频至下限 ----
steps.append(chroma_step(12, "降频至频率下限", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ)]))
steps.append(chroma_step(13, "保持频率下限", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="频率下限", var_id=VAR_LOWER_FREQ)]))
steps.append(delay_step(14, "频率下限保持时间", var_name="各频率点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 五、恢复额定频率 ----
steps.append(chroma_step(15, "恢复额定频率", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(16, "额定频率保持", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(delay_step(17, "额定频率保持时间", var_name="各频率点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 六、收尾 ----
steps.append(chroma_step(18, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(19, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(20, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\输入频率范围测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
-211
View File
@@ -1,211 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\输入电流测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_RATED_FREQ = guid() # 额定频率
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_STABLE_TIME = guid() # 稳定等待时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STABLE_TIME, "IsVisible": True, "IsEditable": True, "Name": "稳定等待时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", "U1")]))
# ---- 二、交流源设置额定输入 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(9, "启动List输出(额定输入)", "启动List输出", []))
# ---- 三、等待OBC启动并稳定在额定负载 ----
steps.append(delay_step(10, "等待OBC启动稳定(额定负载)", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 四、功率分析仪测量输入电流(有效值) ----
steps.append(pw8001_step(11, "测量输入电流有效值", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、收尾 ----
steps.append(chroma_step(12, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(13, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(14, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\输入电流测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
-253
View File
@@ -1,253 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\启动冲击电流试验.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_ACQ_MODE = "DeviceCommand.Devices.TekAcquisitionMode, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ENUM_MEAS_TYPE = "DeviceCommand.Devices.TekMeasurementType, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def tek_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.TektronixMSO", method_name, params)
def delay_step(index, name, ms=None, var_name=None, var_id=None):
if var_name and var_id:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
else:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_RATED_FREQ = guid() # 额定频率
VAR_OSC_CHANNEL = guid() # 示波器电流通道号
VAR_MEAS_NUM = guid() # 测量项编号
VAR_SCREENSHOT_PATH = guid() # 截图保存路径
VAR_TEST_COUNT = guid() # 测试次数
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_OSC_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "示波器电流通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_MEAS_NUM, "IsVisible": True, "IsEditable": True, "Name": "测量项编号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_SCREENSHOT_PATH, "IsVisible": True, "IsEditable": True, "Name": "截图保存路径",
"Type": STRING_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_TEST_COUNT, "IsVisible": True, "IsEditable": True, "Name": "测试次数",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、示波器初始化(峰值检测模式) ----
steps.append(tek_step(1, "设置采集模式为峰值检测", "设置采集模式",
[param("模式", ENUM_ACQ_MODE, 1)])) # PEAKdetect=1
steps.append(tek_step(2, "设置水平刻度(20ms/div)", "设置水平刻度",
[param("秒每格", DOUBLE_TYPE, 0.02)])) # 20ms/div 捕获冲击波形
steps.append(tek_step(3, "设置记录长度", "设置记录长度",
[param("长度", "System.Int64, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", 10000)]))
# ---- 二、添加冲击电流最大值测量 ----
steps.append(tek_step(4, "开启测量项", "开启测量项",
[param("测量项编号", INT_TYPE, use_var=True,
var_name="测量项编号", var_id=VAR_MEAS_NUM)]))
steps.append(tek_step(5, "设置测量类型为MAXimum", "设置测量类型",
[param("测量项编号", INT_TYPE, use_var=True,
var_name="测量项编号", var_id=VAR_MEAS_NUM),
param("类型", ENUM_MEAS_TYPE, 4)])) # MAXimum=4
steps.append(tek_step(6, "设置测量源为电流通道", "设置测量源",
[param("测量项编号", INT_TYPE, use_var=True,
var_name="测量项编号", var_id=VAR_MEAS_NUM),
param("源名称", STRING_TYPE, use_var=True,
var_name="示波器电流通道号", var_id=VAR_OSC_CHANNEL)]))
# ---- 三、交流源设置额定输入(初始0V,准备突然施加) ----
steps.append(chroma_step(7, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(8, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(9, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
# 初始电压设为0V,准备突然施加额定电压
steps.append(chroma_step(10, "配置List交流起始电压(0V)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(11, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(12, "配置List执行时间(快速上升)", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 10.0), # 10ms 快速上升
param("step2", DOUBLE_TYPE, 10.0),
param("step3", DOUBLE_TYPE, 10.0)]))
# ---- 四、启动示波器采集,然后突然施加额定电压 ----
steps.append(tek_step(13, "启动示波器采集", "设置采集状态",
[param("运行", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", True)]))
steps.append(delay_step(14, "等待示波器就绪", 100))
steps.append(chroma_step(15, "启动List输出(突然施加额定电压)", "启动List输出", []))
# ---- 五、等待冲击电流事件完成,读取最大值 ----
steps.append(delay_step(16, "等待冲击电流事件完成", 500))
steps.append(tek_step(17, "查询冲击电流最大值", "查询测量项当前值",
[param("测量项编号", INT_TYPE, use_var=True,
var_name="测量项编号", var_id=VAR_MEAS_NUM)]))
# ---- 六、保存波形截图 ----
steps.append(tek_step(18, "保存冲击电流波形截图", "保存屏幕截图",
[param("上位机文件路径", STRING_TYPE, use_var=True,
var_name="截图保存路径", var_id=VAR_SCREENSHOT_PATH)]))
# ---- 七、收尾 ----
steps.append(tek_step(19, "停止示波器采集", "设置采集状态",
[param("运行", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", False)]))
steps.append(chroma_step(20, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(21, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(22, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\启动冲击电流试验.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
-247
View File
@@ -1,247 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\负载冲击试验.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
BOOL_TYPE = "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_SRC_MODE = "DeviceCommand.Devices.S7200直流源模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ENUM_LOAD_MODE = "DeviceCommand.Devices.S7200负载模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def s7200_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.S7200", method_name, params)
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def delay_step(index, name, ms):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量(Parameters 中 Category=0,供步骤绑定)
VAR_LOAD_CURRENT = guid() # 满载电流(100%负载)
VAR_FULL_HOLD_MS = guid() # 满载保持时间ms
VAR_ZERO_HOLD_MS = guid() # 空载保持时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0
{"ID": VAR_LOAD_CURRENT, "IsVisible": True, "IsEditable": True, "Name": "满载电流",
"Type": DOUBLE_TYPE, "Category": 0, "Value": 100.0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FULL_HOLD_MS, "IsVisible": True, "IsEditable": True, "Name": "满载保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": 100, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_ZERO_HOLD_MS, "IsVisible": True, "IsEditable": True, "Name": "空载保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": 100, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、交流源:额定输入(三相 380V/50Hz,持续输出) ----
steps.append(chroma_step(1, "初始化三相List模式(无限循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "0")]))
steps.append(chroma_step(2, "配置List起始频率(额定50Hz)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, 50.0),
param("step2", DOUBLE_TYPE, 50.0),
param("step3", DOUBLE_TYPE, 50.0)]))
steps.append(chroma_step(3, "配置List交流起始电压(额定380V)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 380.0),
param("step2", DOUBLE_TYPE, 380.0),
param("step3", DOUBLE_TYPE, 380.0)]))
steps.append(chroma_step(4, "配置List交流结束电压(额定380V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 380.0),
param("step2", DOUBLE_TYPE, 380.0),
param("step3", DOUBLE_TYPE, 380.0)]))
steps.append(chroma_step(5, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(6, "启动List输出(额定输入)", "启动List输出", []))
steps.append(delay_step(7, "等待输入稳定", 3000))
# ---- 二、负载(S7200)CC 模式预置 100% 满载电流,斜率最大 ----
steps.append(s7200_step(8, "设置为远程模式", "设置为远程模式", []))
steps.append(s7200_step(9, "清除保护状态", "清除保护状态", []))
steps.append(s7200_step(10, "设置直流源工作模式CC", "设置直流源工作模式",
[param("模式", ENUM_SRC_MODE, 0)])) # VOLTage=0? 见说明
# 说明: S7200直流源模式_枚举 CC 不在该枚举,负载模式用 设置负载工作模式
steps.pop() # 撤销上一步,改用负载模式枚举
steps.append(s7200_step(10, "设置负载工作模式CC", "设置负载工作模式",
[param("模式", ENUM_LOAD_MODE, 0)])) # CC=0
steps.append(s7200_step(11, "设置CC模式电流(100%满载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="满载电流", var_id=VAR_LOAD_CURRENT)]))
steps.append(s7200_step(12, "上升斜率设为负载最大能力", "发送自定义命令",
[param("指令", "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e",
"CURRent:SLEW MAX")]))
steps.append(s7200_step(13, "打开通道(100%负载)", "设置通道开关",
[param("开关", BOOL_TYPE, True)]))
steps.append(delay_step(14, "等待负载稳定", 1000))
# ---- 三、循环 5000 次:0% ↔ 100% 跳变 ----
loop_start_id = guid()
loop_start_step = {
"ID": loop_start_id,
"IsUsed": True,
"Index": 15,
"Name": "循环开始",
"StepType": "循环开始",
"Method": {
"Name": None,
"FullName": None,
"Parameters": [
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "循环次数",
"Type": INT_TYPE, "Category": 0, "Value": 5000, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
],
},
"SubProgram": None,
"LoopCount": 5000,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": "负载0%与100%跳变冲击 5000次",
}
steps.append(loop_start_step)
steps.append(s7200_step(16, "跳变至0%负载(断开通道)", "设置通道开关",
[param("开关", BOOL_TYPE, False)]))
steps.append(method_step(17, "空载保持", "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name="空载保持时间ms", var_id=VAR_ZERO_HOLD_MS)]))
steps.append(s7200_step(18, "跳变至100%负载(接通通道)", "设置通道开关",
[param("开关", BOOL_TYPE, True)]))
steps.append(method_step(19, "满载保持", "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name="满载保持时间ms", var_id=VAR_FULL_HOLD_MS)]))
loop_end_step = {
"ID": guid(),
"IsUsed": True,
"Index": 20,
"Name": "循环结束",
"StepType": "循环结束",
"Method": None,
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": loop_start_id,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
steps.append(loop_end_step)
# ---- 四、收尾 ----
steps.append(s7200_step(21, "关闭负载通道", "设置通道开关",
[param("开关", BOOL_TYPE, False)]))
steps.append(s7200_step(22, "清除保护状态", "清除保护状态", []))
steps.append(chroma_step(23, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\负载冲击试验.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验:能反序列化回基本结构
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. [{s['StepType']}] {s['Name']}")
-306
View File
@@ -1,306 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\输出电流范围测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_LOAD_MODE = "DeviceCommand.Devices.S7200负载模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def s7200_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.S7200", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, ms=None, var_name=None, var_id=None):
if var_name and var_id:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
else:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_FREQ = guid() # 额定频率
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_HOLD_TIME = guid() # 各电压点保持时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_HOLD_TIME, "IsVisible": True, "IsEditable": True, "Name": "各电压点保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源设置额定输入 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(9, "启动List输出(额定输入)", "启动List输出", []))
steps.append(delay_step(10, "等待OBC启动", 5000))
# ---- 三、负载(S7200)初始化 ----
steps.append(s7200_step(11, "设置为远程模式", "设置为远程模式", []))
steps.append(s7200_step(12, "设置负载工作模式CC", "设置负载工作模式",
[param("模式", ENUM_LOAD_MODE, 0)])) # CC=0
# ---- 四、测试点1250Vdc ----
steps.append(s7200_step(13, "设置CC模式电流(250Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)])) # 三相模式最大67A
steps.append(s7200_step(14, "打开通道(加载至250Vdc)", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", True)]))
steps.append(delay_step(15, "等待250Vdc稳定", 3000))
steps.append(pw8001_step(16, "测量250Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(17, "250Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(18, "记录250Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、测试点2300Vdc ----
steps.append(s7200_step(19, "设置CC模式电流(300Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)]))
steps.append(delay_step(20, "等待300Vdc稳定", 3000))
steps.append(pw8001_step(21, "测量300Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(22, "300Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(23, "记录300Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 六、测试点3350Vdc ----
steps.append(s7200_step(24, "设置CC模式电流(350Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)]))
steps.append(delay_step(25, "等待350Vdc稳定", 3000))
steps.append(pw8001_step(26, "测量350Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(27, "350Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(28, "记录350Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 七、测试点4400Vdc ----
steps.append(s7200_step(29, "设置CC模式电流(400Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)]))
steps.append(delay_step(30, "等待400Vdc稳定", 3000))
steps.append(pw8001_step(31, "测量400Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(32, "400Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(33, "记录400Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 八、测试点5450Vdc ----
steps.append(s7200_step(34, "设置CC模式电流(450Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)]))
steps.append(delay_step(35, "等待450Vdc稳定", 3000))
steps.append(pw8001_step(36, "测量450Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(37, "450Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(38, "记录450Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 九、测试点6500Vdc ----
steps.append(s7200_step(39, "设置CC模式电流(500Vdc最大电流)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, 67.0)]))
steps.append(delay_step(40, "等待500Vdc稳定", 3000))
steps.append(pw8001_step(41, "测量500Vdc点输出电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(42, "500Vdc持续运行", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(43, "记录500Vdc最大电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 十、收尾 ----
steps.append(s7200_step(44, "关闭负载通道", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", False)]))
steps.append(chroma_step(45, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(46, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(47, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\输出电流范围测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试电压点(250Vdc - 500Vdc):")
print(" - 250Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print(" - 300Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print(" - 350Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print(" - 400Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print(" - 450Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print(" - 500Vdc:最大持续输出电流 ≤ 67A(三相)/ ≤ 22A(单相)")
print("\n电流精度要求:")
print(" - 0~10A:误差 ≤ ±0.2A")
print(" - 10A以上:误差 ≤ ±2%")
print("\n注意:步骤13-43的电流值当前为67A(三相模式),单相模式请改为22A")
-269
View File
@@ -1,269 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\输出电压范围测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_LOAD_MODE = "DeviceCommand.Devices.S7200负载模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def s7200_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.S7200", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, ms=None, var_name=None, var_id=None):
if var_name and var_id:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
else:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_FREQ = guid() # 额定频率
VAR_VOLT_LOWER = guid() # OBC输出电压下限
VAR_VOLT_UPPER = guid() # OBC输出电压上限
VAR_LOAD_LOWER = guid() # 达到下限电压的负载电流
VAR_LOAD_UPPER = guid() # 达到上限电压的负载电流
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_HOLD_TIME = 60000 # 1min = 60000ms (固定值)
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_VOLT_LOWER, "IsVisible": True, "IsEditable": True, "Name": "OBC输出电压下限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_VOLT_UPPER, "IsVisible": True, "IsEditable": True, "Name": "OBC输出电压上限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LOAD_LOWER, "IsVisible": True, "IsEditable": True, "Name": "达到下限电压的负载电流",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LOAD_UPPER, "IsVisible": True, "IsEditable": True, "Name": "达到上限电压的负载电流",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源设置额定输入 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(9, "启动List输出(额定输入)", "启动List输出", []))
steps.append(delay_step(10, "等待OBC启动", 5000))
# ---- 三、负载(S7200)初始化 ----
steps.append(s7200_step(11, "设置为远程模式", "设置为远程模式", []))
steps.append(s7200_step(12, "设置负载工作模式CC", "设置负载工作模式",
[param("模式", ENUM_LOAD_MODE, 0)])) # CC=0
# ---- 四、调节负载使输出电压为下限值 ----
steps.append(s7200_step(13, "设置CC模式电流(达到下限电压)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="达到下限电压的负载电流", var_id=VAR_LOAD_LOWER)]))
steps.append(s7200_step(14, "打开通道(加载至下限电压)", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", True)]))
steps.append(delay_step(15, "等待输出电压稳定在下限", 3000))
steps.append(pw8001_step(16, "测量下限电压点输出电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(17, "下限电压持续运行1min", VAR_HOLD_TIME))
steps.append(pw8001_step(18, "记录下限电压最大值", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、调节负载使输出电压为上限值 ----
steps.append(s7200_step(19, "设置CC模式电流(达到上限电压)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="达到上限电压的负载电流", var_id=VAR_LOAD_UPPER)]))
steps.append(delay_step(20, "等待输出电压稳定在上限", 3000))
steps.append(pw8001_step(21, "测量上限电压点输出电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(delay_step(22, "上限电压持续运行1min", VAR_HOLD_TIME))
steps.append(pw8001_step(23, "记录上限电压最大值", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 六、收尾 ----
steps.append(s7200_step(24, "关闭负载通道", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", False)]))
steps.append(chroma_step(25, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(26, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(27, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\输出电压范围测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试点:")
print(" - OBC输出电压下限:调节负载使输出电压达到下限,持续运行1min")
print(" - OBC输出电压上限:调节负载使输出电压达到上限,持续运行1min")
print("\n注意:需要用户根据OBC规格填写输出电压上下限及对应的负载电流值")
-240
View File
@@ -1,240 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\三相交流相位不平衡测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_PHASE_VOLT = guid() # 相电压
VAR_FREQ = guid() # 额定频率
VAR_PHASE_DEVIATION = guid() # 相位偏差角度(如5°)
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_STABLE_TIME = guid() # 稳定等待时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_PHASE_VOLT, "IsVisible": True, "IsEditable": True, "Name": "相电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PHASE_DEVIATION, "IsVisible": True, "IsEditable": True, "Name": "相位偏差角度",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STABLE_TIME, "IsVisible": True, "IsEditable": True, "Name": "稳定等待时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源初始化 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
# ---- 三、正常相位测试(基准) ----
# 三相正常相位:L1=0°, L2=120°, L3=240°
steps.append(chroma_step(5, "配置List起始角度(正常相位)", "配置List起始角度Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 120.0),
param("step3", DOUBLE_TYPE, 240.0)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="相电压", var_id=VAR_PHASE_VOLT)]))
steps.append(chroma_step(8, "启动List输出(正常相位)", "启动List输出", []))
steps.append(delay_step(9, "等待正常相位稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(10, "测量正常相位输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 四、相位偏差+5°测试 ----
# L1=0°+5°=5°, L2=120°+5°=125°, L3=240°+5°=245°
steps.append(chroma_step(11, "配置List起始角度(+5°偏差)", "配置List起始角度Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="相位偏差角度", var_id=VAR_PHASE_DEVIATION),
param("step2", DOUBLE_TYPE, 125.0),
param("step3", DOUBLE_TYPE, 245.0)]))
steps.append(delay_step(12, "等待+5°偏差稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(13, "测量+5°偏差输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、相位偏差-5°测试 ----
# L1=0°-5°=-5°(或355°), L2=120°-5°=115°, L3=240°-5°=235°
steps.append(chroma_step(14, "配置List起始角度(-5°偏差)", "配置List起始角度Async",
[param("step1", DOUBLE_TYPE, -5.0),
param("step2", DOUBLE_TYPE, 115.0),
param("step3", DOUBLE_TYPE, 235.0)]))
steps.append(delay_step(15, "等待-5°偏差稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(16, "测量-5°偏差输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 六、恢复正常相位 ----
steps.append(chroma_step(17, "配置List起始角度(恢复正常)", "配置List起始角度Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 120.0),
param("step3", DOUBLE_TYPE, 240.0)]))
steps.append(delay_step(18, "等待恢复正常", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 七、收尾 ----
steps.append(chroma_step(19, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(20, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(21, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\三相交流相位不平衡测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试相位点:")
print(" - 正常相位:L1=0°, L2=120°, L3=240°")
print(" - +5°偏差:L1=5°, L2=125°, L3=245°")
print(" - -5°偏差:L1=-5°, L2=115°, L3=235°")
-267
View File
@@ -1,267 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\功率因数测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ENUM_LOAD_MODE = "DeviceCommand.Devices.S7200负载模式_枚举, DeviceCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def s7200_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.S7200", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, ms=None, var_name=None, var_id=None):
if var_name and var_id:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
else:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_RATED_FREQ = guid() # 额定频率
VAR_RATED_POWER = guid() # OBC额定功率
VAR_50PCT_POWER = guid() # 50%负载功率
VAR_300W_OR_5PCT = guid() # 300W或5%功率(取大者)
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_STABLE_TIME = guid() # 稳定等待时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_RATED_POWER, "IsVisible": True, "IsEditable": True, "Name": "OBC额定功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_50PCT_POWER, "IsVisible": True, "IsEditable": True, "Name": "50%负载功率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_300W_OR_5PCT, "IsVisible": True, "IsEditable": True, "Name": "300W或5%功率(取大者)",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STABLE_TIME, "IsVisible": True, "IsEditable": True, "Name": "稳定等待时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化(IEC模式,包含功率因数测量) ----
steps.append(pw8001_step(1, "设置测试模式IEC", "设置测试模式_IEC", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源设置额定输入 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_RATED_FREQ)]))
steps.append(chroma_step(6, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(7, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(9, "启动List输出(额定输入)", "启动List输出", []))
steps.append(delay_step(10, "等待OBC启动", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 三、负载(S7200)设置:50%负载点测试 ----
steps.append(s7200_step(11, "设置为远程模式", "设置为远程模式", []))
steps.append(s7200_step(12, "设置负载工作模式CC", "设置负载工作模式",
[param("模式", ENUM_LOAD_MODE, 0)])) # CC=0
steps.append(s7200_step(13, "设置CC模式电流(50%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="50%负载功率", var_id=VAR_50PCT_POWER)]))
steps.append(s7200_step(14, "打开通道(50%负载)", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", True)]))
steps.append(delay_step(15, "等待50%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 四、测量50%负载点的功率因数 ----
steps.append(pw8001_step(16, "测量50%负载有功功率", "查询功率_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(pw8001_step(17, "测量50%负载功率因数", "发送自定义命令",
[param("命令", STRING_TYPE, ":MEAS:PF?")]))
steps.append(delay_step(18, "记录50%负载数据", 1000))
# ---- 五、切换到300W或5%负载点测试 ----
steps.append(s7200_step(19, "设置CC模式电流(300W或5%负载)", "设置CC模式电流",
[param("电流", DOUBLE_TYPE, use_var=True,
var_name="300W或5%功率(取大者)", var_id=VAR_300W_OR_5PCT)]))
steps.append(delay_step(20, "等待300W/5%负载稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 六、测量300W或5%负载点的功率因数 ----
steps.append(pw8001_step(21, "测量300W/5%负载有功功率", "查询功率_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(pw8001_step(22, "测量300W/5%负载功率因数", "发送自定义命令",
[param("命令", STRING_TYPE, ":MEAS:PF?")]))
steps.append(delay_step(23, "记录300W/5%负载数据", 1000))
# ---- 七、收尾 ----
steps.append(s7200_step(24, "关闭负载通道", "设置通道开关",
[param("开关", "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", False)]))
steps.append(chroma_step(25, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(26, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(27, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\功率因数测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n判定标准:")
print(" - 50%负载点:功率因数 ≥ 0.99")
print(" - 300W或5%负载点:功率因数 ≥ 0.9")
-256
View File
@@ -1,256 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\输入电压范围测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
BOOL_TYPE = "System.Boolean, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def delay_step(index, name, ms=None, var_name=None, var_id=None):
if var_name and var_id:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
else:
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, str(ms))])
# 程序变量(Parameters 中 Category=0,供步骤绑定)
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_UPPER_VOLT = guid() # 输入电压上限
VAR_LOWER_VOLT = guid() # 输入电压下限
VAR_FREQ = guid() # 额定频率
VAR_HOLD_TIME = guid() # 各电压点保持时间(ms)
VAR_STEP_VOLT = guid() # 电压步进值
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_UPPER_VOLT, "IsVisible": True, "IsEditable": True, "Name": "输入电压上限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LOWER_VOLT, "IsVisible": True, "IsEditable": True, "Name": "输入电压下限",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_HOLD_TIME, "IsVisible": True, "IsEditable": True, "Name": "各电压点保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STEP_VOLT, "IsVisible": True, "IsEditable": True, "Name": "电压步进值",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、初始化交流源 ----
steps.append(chroma_step(1, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(2, "配置List起始频率", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(3, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
# ---- 二、额定电压启动OBC ----
steps.append(chroma_step(4, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(5, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(6, "启动List输出(额定电压)", "启动List输出", []))
steps.append(delay_step(7, "等待OBC启动稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 三、逐步升压至上限 ----
steps.append(chroma_step(8, "升压至上限电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT)]))
steps.append(chroma_step(9, "保持上限电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压上限", var_id=VAR_UPPER_VOLT)]))
steps.append(delay_step(10, "上限电压保持时间", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 四、逐步降压至下限 ----
steps.append(chroma_step(11, "降压至下限电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT)]))
steps.append(chroma_step(12, "保持下限电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="输入电压下限", var_id=VAR_LOWER_VOLT)]))
steps.append(delay_step(13, "下限电压保持时间", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 五、恢复额定电压 ----
steps.append(chroma_step(14, "恢复额定电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(15, "额定电压保持", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(delay_step(16, "额定电压保持时间", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 六、收尾 ----
steps.append(chroma_step(17, "停止输出", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(18, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(19, "清除错误队列", "清除错误队列", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\输入电压范围测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
-299
View File
@@ -1,299 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\限压特性.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_RATED_VOLT = guid() # 额定输入电压
VAR_FREQ = guid() # 额定频率
VAR_LIMIT_VOLT = guid() # 限压保护阈值(预期值)
VAR_STEP_VOLT = guid() # 电压步进值
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_HOLD_TIME = guid() # 各电压点保持时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定输入电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_LIMIT_VOLT, "IsVisible": True, "IsEditable": True, "Name": "限压保护阈值(预期)",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STEP_VOLT, "IsVisible": True, "IsEditable": True, "Name": "电压步进值",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_HOLD_TIME, "IsVisible": True, "IsEditable": True, "Name": "各电压点保持时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源初始化 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(6, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
# ---- 三、额定电压启动OBC ----
steps.append(chroma_step(7, "配置List交流起始电压(额定)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(8, "配置List交流结束电压(额定)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(9, "启动List输出(额定电压)", "启动List输出", []))
steps.append(delay_step(10, "等待OBC启动稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(11, "测量额定电压点输入电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 四、逐步升压至限压阈值(示例:额定+5%、+10%、+15%... ----
# 这里使用固定示例值,实际需要根据用户填写的额定电压和步进值计算
# 假设额定220V,步进10V,测试点:231V(105%)、242V(110%)、253V(115%)、264V(120%)
steps.append(chroma_step(12, "升压至105%额定电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 231.0),
param("step2", DOUBLE_TYPE, 231.0),
param("step3", DOUBLE_TYPE, 231.0)]))
steps.append(chroma_step(13, "保持105%额定电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 231.0),
param("step2", DOUBLE_TYPE, 231.0),
param("step3", DOUBLE_TYPE, 231.0)]))
steps.append(delay_step(14, "等待105%电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(15, "测量105%电压点输入电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(chroma_step(16, "升压至110%额定电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 242.0),
param("step2", DOUBLE_TYPE, 242.0),
param("step3", DOUBLE_TYPE, 242.0)]))
steps.append(chroma_step(17, "保持110%额定电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 242.0),
param("step2", DOUBLE_TYPE, 242.0),
param("step3", DOUBLE_TYPE, 242.0)]))
steps.append(delay_step(18, "等待110%电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(19, "测量110%电压点输入电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(chroma_step(20, "升压至115%额定电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 253.0),
param("step2", DOUBLE_TYPE, 253.0),
param("step3", DOUBLE_TYPE, 253.0)]))
steps.append(chroma_step(21, "保持115%额定电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 253.0),
param("step2", DOUBLE_TYPE, 253.0),
param("step3", DOUBLE_TYPE, 253.0)]))
steps.append(delay_step(22, "等待115%电压稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(23, "测量115%电压点输入电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
steps.append(chroma_step(24, "升压至120%额定电压(限压点)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 264.0),
param("step2", DOUBLE_TYPE, 264.0),
param("step3", DOUBLE_TYPE, 264.0)]))
steps.append(chroma_step(25, "保持120%额定电压(限压点)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 264.0),
param("step2", DOUBLE_TYPE, 264.0),
param("step3", DOUBLE_TYPE, 264.0)]))
steps.append(delay_step(26, "等待限压点稳定", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
steps.append(pw8001_step(27, "测量限压点输入电压", "查询电压_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、降压恢复额定电压 ----
steps.append(chroma_step(28, "降压至额定电压", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(29, "保持额定电压", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定输入电压", var_id=VAR_RATED_VOLT)]))
steps.append(delay_step(30, "等待恢复正常", var_name="各电压点保持时间ms", var_id=VAR_HOLD_TIME))
# ---- 六、收尾 ----
steps.append(chroma_step(31, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(32, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(33, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\限压特性.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试电压点(示例,220V系统):")
print(" - 额定电压:220V (100%)")
print(" - 105%额定:231V")
print(" - 110%额定:242V")
print(" - 115%额定:253V")
print(" - 120%额定:264V (限压测试点)")
print("\n注意:步骤12-27的电压值为示例(220V系统),请根据实际额定电压和限压阈值调整")
-257
View File
@@ -1,257 +0,0 @@
# -*- coding: utf-8 -*-
"""生成 D:\\ACP\\测试项\\三相交流电压不平衡测试.ACP"""
import json
import uuid
CT_TYPE = "System.Threading.CancellationToken, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
INT_TYPE = "System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
DOUBLE_TYPE = "System.Double, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
STRING_TYPE = "System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"
ZERO_GUID = "00000000-0000-0000-0000-000000000000"
def guid():
return str(uuid.uuid4())
def param(name, ptype, value=None, use_var=False, var_name=None, var_id=None):
return {
"ID": guid(),
"IsVisible": True,
"IsEditable": True,
"Name": name,
"Type": ptype,
"Category": 0,
"Value": value,
"LowerLimit": None,
"UpperLimit": None,
"Result": True,
"IsUseVar": use_var,
"VariableName": var_name,
"VariableID": var_id,
}
def ct_param():
return param("ct", CT_TYPE)
def method_step(index, name, full_name, method_name, params):
return {
"ID": guid(),
"IsUsed": True,
"Index": index,
"Name": name,
"StepType": "方法",
"Method": {
"Name": method_name,
"FullName": full_name,
"Parameters": params + [ct_param()],
},
"SubProgram": None,
"LoopCount": None,
"LoopStartStepId": None,
"OKExpression": None,
"GotoSettingString": "",
"OKGotoStepID": ZERO_GUID,
"NGGotoStepID": ZERO_GUID,
"Description": None,
}
def chroma_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Devices.Chroma61800", method_name, params)
def pw8001_step(index, name, method_name, params):
return method_step(index, name, "DeviceCommand.Device.PW8001", method_name, params)
def delay_step(index, name, var_name=None, var_id=None):
return method_step(index, name, "Command.Delay", "Delay_ms",
[param("millisecond", INT_TYPE, use_var=True,
var_name=var_name, var_id=var_id)])
# 程序变量
VAR_RATED_VOLT = guid() # 额定相电压
VAR_FREQ = guid() # 额定频率
VAR_UNBALANCE_PCT = guid() # 电压不平衡度(%)
VAR_PW_CHANNEL = guid() # 功率分析仪通道号
VAR_STABLE_TIME = guid() # 稳定等待时间ms
program_params = [
# 标准系统参数(Category=2
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "台架序号",
"Type": INT_TYPE, "Category": 2, "Value": 1, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "CAN通道",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "示波器通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道1",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": guid(), "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道2",
"Type": INT_TYPE, "Category": 2, "Value": 0, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
# 测试项可调变量(Category=0)- 用户手动填值
{"ID": VAR_RATED_VOLT, "IsVisible": True, "IsEditable": True, "Name": "额定相电压",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_FREQ, "IsVisible": True, "IsEditable": True, "Name": "额定频率",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_UNBALANCE_PCT, "IsVisible": True, "IsEditable": True, "Name": "电压不平衡度(%)",
"Type": DOUBLE_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_PW_CHANNEL, "IsVisible": True, "IsEditable": True, "Name": "功率分析仪通道号",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
{"ID": VAR_STABLE_TIME, "IsVisible": True, "IsEditable": True, "Name": "稳定等待时间ms",
"Type": INT_TYPE, "Category": 0, "Value": None, "LowerLimit": None,
"UpperLimit": None, "Result": True, "IsUseVar": False,
"VariableName": None, "VariableID": None},
]
steps = []
# ---- 一、功率分析仪初始化 ----
steps.append(pw8001_step(1, "设置测试模式WIDE", "设置测试模式_WIDE", []))
steps.append(pw8001_step(2, "设置同步源U1", "设置同步源",
[param("", STRING_TYPE, "U1")]))
# ---- 二、交流源初始化 ----
steps.append(chroma_step(3, "初始化三相List模式(单次循环)", "初始化三相List模式",
[param("loopCount", INT_TYPE, "1")]))
steps.append(chroma_step(4, "配置List起始频率(额定)", "配置List起始频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(5, "配置List结束频率(额定)", "配置List结束频率Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定频率", var_id=VAR_FREQ)]))
steps.append(chroma_step(6, "配置List执行时间", "配置List执行时间Async",
[param("step1", DOUBLE_TYPE, 100.0),
param("step2", DOUBLE_TYPE, 100.0),
param("step3", DOUBLE_TYPE, 100.0)]))
steps.append(chroma_step(7, "配置List起始角度(标准120°)", "配置List起始角度Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 120.0),
param("step3", DOUBLE_TYPE, 240.0)]))
# ---- 三、正常电压测试(基准) ----
# 三相额定电压:L1=L2=L3=额定
steps.append(chroma_step(8, "配置List交流起始电压(额定平衡)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(9, "配置List交流结束电压(额定平衡)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(10, "启动List输出(额定平衡电压)", "启动List输出", []))
steps.append(delay_step(11, "等待额定电压稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(12, "测量额定平衡电压输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 四、5%电压不平衡测试(L1相降低5%) ----
# 假设额定220V5%不平衡=11VL1=209VL2=L3=220V
# 这里使用固定值示例,实际需要根据用户填写的额定电压和不平衡度计算
steps.append(chroma_step(13, "配置List交流起始电压(5%不平衡-L1降低)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 209.0), # 220V * 0.95 = 209V
param("step2", DOUBLE_TYPE, 220.0),
param("step3", DOUBLE_TYPE, 220.0)]))
steps.append(chroma_step(14, "配置List交流结束电压(5%不平衡-L1降低)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 209.0),
param("step2", DOUBLE_TYPE, 220.0),
param("step3", DOUBLE_TYPE, 220.0)]))
steps.append(delay_step(15, "等待5%不平衡电压稳定", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
steps.append(pw8001_step(16, "测量5%不平衡电压输入电流", "查询电流_不含变比",
[param("通道号", INT_TYPE, use_var=True,
var_name="功率分析仪通道号", var_id=VAR_PW_CHANNEL)]))
# ---- 五、恢复正常电压 ----
steps.append(chroma_step(17, "配置List交流起始电压(恢复正常)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT)]))
steps.append(chroma_step(18, "配置List交流结束电压(恢复正常)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step2", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT),
param("step3", DOUBLE_TYPE, use_var=True,
var_name="额定相电压", var_id=VAR_RATED_VOLT)]))
steps.append(delay_step(19, "等待恢复正常", var_name="稳定等待时间ms", var_id=VAR_STABLE_TIME))
# ---- 六、收尾 ----
steps.append(chroma_step(20, "停止输出(电压归零)", "配置List交流起始电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(chroma_step(21, "配置List交流结束电压(0V)", "配置List交流结束电压Async",
[param("step1", DOUBLE_TYPE, 0.0),
param("step2", DOUBLE_TYPE, 0.0),
param("step3", DOUBLE_TYPE, 0.0)]))
steps.append(pw8001_step(22, "清除状态", "清除状态", []))
program = {
"ID": guid(),
"StepCollection": steps,
"ErrorStepCollection": [],
"Parameters": program_params,
}
out_path = r"D:\ACP\测试项\三相交流电压不平衡测试.ACP"
with open(out_path, "w", encoding="utf-8") as f:
json.dump(program, f, ensure_ascii=False, indent=2)
# 校验
with open(out_path, encoding="utf-8") as f:
data = json.load(f)
print(f"已生成: {out_path}")
print(f"步骤数: {len(data['StepCollection'])}, 参数数: {len(data['Parameters'])}")
print("\n需要手动填写的变量参数:")
for p in data["Parameters"]:
if p["Category"] == 0:
print(f" - {p['Name']} ({p['Type'].split(',')[0].split('.')[-1]})")
print("\n步骤流程:")
for s in data["StepCollection"]:
print(f" {s['Index']:>2}. {s['Name']}")
print("\n测试电压点:")
print(" - 额定平衡电压:L1=L2=L3=额定")
print(" - 5%不平衡电压:L1=95%额定,L2=L3=额定(示例:209V/220V/220V")
print("\n注意:步骤13-14的电压值为示例(220V系统),请根据实际额定电压调整")
+1
View File
@@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+63
View File
@@ -0,0 +1,63 @@
using Common.Attributes;
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
namespace Command
{
/// <summary>
/// 对话框命令:在测试流程中弹出提示窗口。
/// 命令库为纯 .NET 类库,不直接引用 WPF 程序集;
/// 实际的弹窗能力由宿主程序(ACP 外壳)在启动时注入到 <see cref="弹窗处理器"/> 委托中实现(依赖倒置)。
/// </summary>
[ACPCommand]
public static class CommandDialog
{
/// <summary>
/// 弹窗类型
/// </summary>
public enum DialogType
{
/// <summary>
/// 信息提示
/// </summary>
Info,
/// <summary>
/// 警告提示
/// </summary>
Warning,
/// <summary>
/// 错误提示
/// </summary>
Error
}
/// <summary>
/// 弹窗处理委托(参数依次为:弹窗类型、弹窗详细、是否阻塞、自动关闭秒数、取消令牌)。
/// 由宿主程序启动时注入实现;未注入时弹窗命令降级为仅输出日志,不会抛异常。
/// </summary>
[Browsable(false)]
public static Func<DialogType, string, bool, float, CancellationToken, Task> ;
/// <summary>
/// 弹窗:在界面上显示一个提示对话框。
/// </summary>
/// <param name="弹窗类型">弹窗样式:Info 信息 / Warning 警告 / Error 错误</param>
/// <param name="弹窗详细">弹窗中显示的详细内容</param>
/// <param name="是否阻塞">true = 步骤暂停,等待用户关闭(或自动关闭)后才继续;false = 弹出后步骤立即继续</param>
/// <param name="自动关闭秒数">大于 0 时,弹窗在指定秒数后自动关闭;小于等于 0 时不自动关闭,需用户手动关闭</param>
/// <param name="ct">异步取消令牌</param>
public static async Task (DialogType , string , bool , float , CancellationToken ct)
{
var handler = ;
if (handler == null)
{
Console.WriteLine($"[弹窗命令] 宿主未注入弹窗处理器,跳过弹窗:{弹窗详细}");
return;
}
await handler(, , , , ct);
}
}
}
+1
View File
@@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+25 -1
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -23,7 +24,7 @@ namespace Common.Tools
{ {
foreach (var kvp in processedVariables) foreach (var kvp in processedVariables)
{ {
expr.Parameters[kvp.Key] = kvp.Value; expr.Parameters[kvp.Key] = NormalizeValue(kvp.Value);
} }
} }
@@ -95,6 +96,29 @@ namespace Common.Tools
return (processedExpression, newVariables); return (processedExpression, newVariables);
} }
/// <summary>
/// 归一化变量取值:输入变量的值常以字符串形式保存(如 "10"),
/// 若直接参与比较,NCalc 会按字符串逐位比较("10" > "5" 为假),
/// 导致大于/小于判断失真。此处将可解析为数字/布尔的字符串转换为对应类型,
/// 使比较按数值语义进行;无法解析的字符串保持原样。
/// </summary>
private static object? NormalizeValue(object? value)
{
if (value is string s)
{
if (double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)
|| double.TryParse(s, out d))
{
return d;
}
if (bool.TryParse(s, out var b))
{
return b;
}
}
return value;
}
// 检查字符串是否包含中文字符 // 检查字符串是否包含中文字符
private static bool ContainsChinese(string text) private static bool ContainsChinese(string text)
{ {
+1
View File
@@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
+334 -9
View File
@@ -2,33 +2,358 @@
using DeviceCommand.Base; using DeviceCommand.Base;
using Model.Models; using Model.Models;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Text.RegularExpressions;
using System.Text; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary> /// <summary>
/// 负载 一对三 /// ANEVH 操作模式枚举
/// </summary>
public enum ANEVH操作模式_枚举
{
/// <summary>定值模式</summary>
FIXED,
/// <summary>序列测试模式</summary>
LIST
}
/// <summary>
/// ANEVH 系列双向可编程直流电源(一拖三),基于 TCP 通信,使用 SCPI 指令集。
/// 作为高压源载一体机使用:同时具备直流电源输出(SOURce)与电子负载(SINK)能力。
/// </summary> /// </summary>
[ACPCommand] [ACPCommand]
public class ANEVH80 : Tcp public class ANEVH80 : Tcp
{ {
// Chroma SCPI 默认使用 \n 作为结束符 // SCPI 指令默认使用 \n 作为结束符
private const string ScpiDelimiter = "\n"; private const string ScpiDelimiter = "\n";
/// <summary>
/// 构造函数,初始化 TCP 连接配置。
/// </summary>
/// <param name="config">TCP 连接配置(IP、端口等)</param>
public ANEVH80(TcpConfig config) : base(config) public ANEVH80(TcpConfig config) : base(config)
{ {
} }
/// <summary> /// <summary>
/// 占位符,可能的操作先占位 /// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary> /// </summary>
public virtual async Task<double> (CancellationToken ct = default) /// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{ {
string resp = await WriteReadAsync($"MEAS:FREQ?{ScpiDelimiter}", ScpiDelimiter, ct); if (string.IsNullOrWhiteSpace(raw)) return 0;
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
} }
#region 1.
/// <summary>
/// 清除错误队列 (*CLS)
/// </summary>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(CancellationToken ct = default)
{
await SendAsync($"*CLS{ScpiDelimiter}", ct);
}
/// <summary>
/// 重设仪器为出厂默认状态 (*RST)
/// </summary>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(CancellationToken ct = default)
{
await SendAsync($"*RST{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询仪器识别码 (*IDN?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>设备标识字符串(厂商,型号,序列号,固件版本)</returns>
public virtual async Task<string> Async(CancellationToken ct = default)
{
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
}
/// <summary>
/// 查询错误信息 (SYST:ERR?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>错误信息字符串</returns>
public virtual async Task<string> Async(CancellationToken ct = default)
{
return await WriteReadAsync($"SYST:ERR?{ScpiDelimiter}", ScpiDelimiter, ct);
}
#endregion
#region 2. (OUTPut )
/// <summary>
/// 设置输出开关状态 (OUTP ON / OFF)
/// </summary>
/// <param name="isOn">true: 开启输出, false: 关闭输出</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(bool isOn, CancellationToken ct = default)
{
string state = isOn ? "ON" : "OFF";
await SendAsync($"OUTP {state}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置操作模式 (OUTP:MODE)
/// </summary>
/// <param name="mode">操作模式枚举 (FIXED: 定值 | LIST: 序列测试)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(ANEVH操作模式_枚举 mode, CancellationToken ct = default)
{
await SendAsync($"OUTP:MODE {mode}{ScpiDelimiter}", ct);
}
#endregion
#region 3. (SOURce )
/// <summary>
/// 设置源输出电压 (SOUR:VOLT)
/// </summary>
/// <param name="voltage">电压值 (单位: V)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double voltage, CancellationToken ct = default)
{
string valStr = voltage.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:VOLT {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询源设定电压 (SOUR:VOLT?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>设定电压值 (单位: V)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SOUR:VOLT?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 设置源正向限流 (SOUR:CURR)
/// </summary>
/// <param name="current">电流值 (单位: A,正值)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double current, CancellationToken ct = default)
{
string valStr = current.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:CURR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询源设定电流 (SOUR:CURR?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>设定电流值 (单位: A)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SOUR:CURR?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 设置源正向限功率 (SOUR:POW)
/// </summary>
/// <param name="power">功率值 (单位: W,正值)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double power, CancellationToken ct = default)
{
string valStr = power.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:POW {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询源设定功率 (SOUR:POW?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>设定功率值 (单位: W)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SOUR:POW?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 设置电压上升斜率 (SOUR:VOLT:SLEW:POS)
/// </summary>
/// <param name="slewRate">电压上升速率 (单位: V/ms)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double slewRate, CancellationToken ct = default)
{
string valStr = slewRate.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:VOLT:SLEW:POS {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置电压下降斜率 (SOUR:VOLT:SLEW:NEG)
/// </summary>
/// <param name="slewRate">电压下降速率 (单位: V/ms)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double slewRate, CancellationToken ct = default)
{
string valStr = slewRate.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:VOLT:SLEW:NEG {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置电流上升斜率 (SOUR:CURR:SLEW:POS)
/// </summary>
/// <param name="slewRate">电流上升速率 (单位: A/ms)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double slewRate, CancellationToken ct = default)
{
string valStr = slewRate.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:CURR:SLEW:POS {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置电流下降斜率 (SOUR:CURR:SLEW:NEG)
/// </summary>
/// <param name="slewRate">电流下降速率 (单位: A/ms)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double slewRate, CancellationToken ct = default)
{
string valStr = slewRate.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:CURR:SLEW:NEG {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置过压保护值 (SOUR:VOLT:PROT)
/// </summary>
/// <param name="voltage">过压保护阈值 (单位: V)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double voltage, CancellationToken ct = default)
{
string valStr = voltage.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:VOLT:PROT {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置过流保护值 (SOUR:CURR:PROT)
/// </summary>
/// <param name="current">过流保护阈值 (单位: A)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double current, CancellationToken ct = default)
{
string valStr = current.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:CURR:PROT {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置过功率保护值 (SOUR:POW:PROT)
/// </summary>
/// <param name="power">过功率保护阈值 (单位: W)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double power, CancellationToken ct = default)
{
string valStr = power.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SOUR:POW:PROT {valStr}{ScpiDelimiter}", ct);
}
#endregion
#region 4. (SINK )
/// <summary>
/// 设置负载吸收电流 (SINK:CURR)
/// </summary>
/// <param name="current">负载电流值 (单位: A)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double current, CancellationToken ct = default)
{
string valStr = current.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SINK:CURR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询负载设定电流 (SINK:CURR?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>负载电流设定值 (单位: A)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SINK:CURR?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 设置负载吸收功率 (SINK:POW)
/// </summary>
/// <param name="power">负载功率值 (单位: W)</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(double power, CancellationToken ct = default)
{
string valStr = power.ToString("0.###", CultureInfo.InvariantCulture);
await SendAsync($"SINK:POWer {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询负载设定功率 (SINK:POW?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>负载功率设定值 (单位: W)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SINK:POWer?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
#endregion
#region 5. (MEASure )
/// <summary>
/// 查询测量电压 (MEAS:VOLT?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>实际输出电压 (单位: V,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"MEAS:VOLT?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 查询测量电流 (MEAS:CURR?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>实际输出电流 (单位: A,保留两位小数,正值为源模式,负值为负载模式)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"MEAS:CURR?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
/// <summary>
/// 查询测量功率 (MEAS:POW?)
/// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>实际输出功率 (单位: W,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"MEAS:POW?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
}
#endregion
} }
} }
+212 -26
View File
@@ -3,6 +3,7 @@ using DeviceCommand.Base;
using Model.Models; using Model.Models;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -21,10 +22,27 @@ namespace DeviceCommand.Devices
{ {
} }
/// <summary>
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary>
/// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return 0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
}
#region 1. #region 1.
/// <summary> /// <summary>
/// 清除错误队列 (*CLS)[cite: 1] /// 清除错误队列 (*CLS)
/// </summary> /// </summary>
public virtual async Task Async(CancellationToken ct = default) public virtual async Task Async(CancellationToken ct = default)
{ {
@@ -32,7 +50,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 重设仪器为初始状态 (*RST)[cite: 1] /// 重设仪器为初始状态 (*RST)
/// </summary> /// </summary>
public virtual async Task Async(CancellationToken ct = default) public virtual async Task Async(CancellationToken ct = default)
{ {
@@ -40,7 +58,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 查询仪器识别码 (*IDN?)[cite: 1] /// 查询仪器识别码 (*IDN?)
/// </summary> /// </summary>
public virtual async Task<string> Async(CancellationToken ct = default) public virtual async Task<string> Async(CancellationToken ct = default)
{ {
@@ -49,10 +67,10 @@ namespace DeviceCommand.Devices
#endregion #endregion
#region 2. (OUTPut )[cite: 1] #region 2. (OUTPut )
/// <summary> /// <summary>
/// 设置输出开关状态 (OUTP ON / OFF)[cite: 1] /// 设置输出开关状态 (OUTP ON / OFF)
/// </summary> /// </summary>
/// <param name="isOn">true: 开启输出, false: 关闭输出</param> /// <param name="isOn">true: 开启输出, false: 关闭输出</param>
public virtual async Task Async(bool isOn, CancellationToken ct = default) public virtual async Task Async(bool isOn, CancellationToken ct = default)
@@ -62,7 +80,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置输出耦合模式 (OUTP:COUP AC | DC | ACDC)[cite: 1] /// 设置输出耦合模式 (OUTP:COUP AC | DC | ACDC)
/// </summary> /// </summary>
public virtual async Task Async(string couplingMode, CancellationToken ct = default) public virtual async Task Async(string couplingMode, CancellationToken ct = default)
{ {
@@ -71,7 +89,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置输出继电器状态 (OUTP:REL ON | OFF)[cite: 1] /// 设置输出继电器状态 (OUTP:REL ON | OFF)
/// </summary> /// </summary>
public virtual async Task Async(bool isOn, CancellationToken ct = default) public virtual async Task Async(bool isOn, CancellationToken ct = default)
{ {
@@ -81,10 +99,10 @@ namespace DeviceCommand.Devices
#endregion #endregion
#region 3. (SOURCE )[cite: 1] #region 3. (SOURCE )
/// <summary> /// <summary>
/// 设置交流电压 (VOLT:AC)[cite: 1] /// 设置交流电压 (VOLT:AC)
/// </summary> /// </summary>
/// <param name="voltage">电压值 (0 ~ 300V 或高压选配范围)</param> /// <param name="voltage">电压值 (0 ~ 300V 或高压选配范围)</param>
public virtual async Task Async(double voltage, CancellationToken ct = default) public virtual async Task Async(double voltage, CancellationToken ct = default)
@@ -94,7 +112,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置直流电压 (VOLT:DC)[cite: 1] /// 设置直流电压 (VOLT:DC)
/// </summary> /// </summary>
public virtual async Task Async(double voltage, CancellationToken ct = default) public virtual async Task Async(double voltage, CancellationToken ct = default)
{ {
@@ -103,7 +121,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置输出频率 (FREQ)[cite: 1] /// 设置输出频率 (FREQ)
/// </summary> /// </summary>
/// <param name="frequency">频率值 (Hz)</param> /// <param name="frequency">频率值 (Hz)</param>
public virtual async Task Async(double frequency, CancellationToken ct = default) public virtual async Task Async(double frequency, CancellationToken ct = default)
@@ -113,7 +131,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置波形选择 (FUNC:SHAP:A SINE / SQUA / CSIN 等)[cite: 1] /// 设置波形选择 (FUNC:SHAP:A SINE / SQUA / CSIN 等)
/// </summary> /// </summary>
public virtual async Task Async(string shape, CancellationToken ct = default) public virtual async Task Async(string shape, CancellationToken ct = default)
{ {
@@ -122,10 +140,10 @@ namespace DeviceCommand.Devices
#endregion #endregion
#region 4. (INST & PHASE )[cite: 1] #region 4. (INST & PHASE )
/// <summary> /// <summary>
/// 设置单/三相工作模式 (INST:PHAS THREE / SINGLE)[cite: 1] /// 设置单/三相工作模式 (INST:PHAS THREE / SINGLE)
/// </summary> /// </summary>
/// <param name="isThreePhase">true: 三相模式, false: 单相模式</param> /// <param name="isThreePhase">true: 三相模式, false: 单相模式</param>
public virtual async Task Async(bool isThreePhase, CancellationToken ct = default) public virtual async Task Async(bool isThreePhase, CancellationToken ct = default)
@@ -135,7 +153,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置三相模式下的相互关系 (PHASE:THREE INDEPEND / SAMEFREQ / BALANCE)[cite: 1] /// 设置三相模式下的相互关系 (PHASE:THREE INDEPEND / SAMEFREQ / BALANCE)
/// </summary> /// </summary>
public virtual async Task Async(string relation, CancellationToken ct = default) public virtual async Task Async(string relation, CancellationToken ct = default)
{ {
@@ -144,7 +162,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置输出开启相位角 (PHASE:ON)[cite: 1] /// 设置输出开启相位角 (PHASE:ON)
/// </summary> /// </summary>
public virtual async Task Async(double degree, CancellationToken ct = default) public virtual async Task Async(double degree, CancellationToken ct = default)
{ {
@@ -153,7 +171,7 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置输出关闭相位角 (PHASE:OFF)[cite: 1] /// 设置输出关闭相位角 (PHASE:OFF)
/// </summary> /// </summary>
public virtual async Task Async(double degree, CancellationToken ct = default) public virtual async Task Async(double degree, CancellationToken ct = default)
{ {
@@ -163,42 +181,210 @@ namespace DeviceCommand.Devices
#endregion #endregion
#region 5. (MEASure )[cite: 1] #region 5. (MEASure )
/// <summary> /// <summary>
/// 查询交流电压均方根值 (MEAS:VOLT:AC?)[cite: 1] /// 查询交流电压均方根值 (MEAS:VOLT:AC?)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>交流电压均方根值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default) public virtual async Task<double> Async(CancellationToken ct = default)
{ {
string resp = await WriteReadAsync($"MEAS:VOLT:AC?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($"MEAS:VOLT:AC?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0; return ExtractDouble(resp);
} }
/// <summary> /// <summary>
/// 查询交流电流均方根值 (MEAS:CURR:AC?)[cite: 1] /// 查询交流电流均方根值 (MEAS:CURR:AC?)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>交流电流均方根值 (单位: A,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default) public virtual async Task<double> Async(CancellationToken ct = default)
{ {
string resp = await WriteReadAsync($"MEAS:CURR:AC?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($"MEAS:CURR:AC?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0; return ExtractDouble(resp);
} }
/// <summary> /// <summary>
/// 查询输出频率 (MEAS:FREQ?)[cite: 1] /// 查询输出频率 (MEAS:FREQ?)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>输出频率值 (单位: Hz,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default) public virtual async Task<double> Async(CancellationToken ct = default)
{ {
string resp = await WriteReadAsync($"MEAS:FREQ?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($"MEAS:FREQ?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0; return ExtractDouble(resp);
} }
/// <summary> /// <summary>
/// 查询真实输出功率 (MEAS:POW:AC?)[cite: 1] /// 查询真实输出功率 (MEAS:POW:AC?)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>真实输出功率值 (单位: W,保留两位小数)</returns>
public virtual async Task<double> Async(CancellationToken ct = default) public virtual async Task<double> Async(CancellationToken ct = default)
{ {
string resp = await WriteReadAsync($"MEAS:POW:AC?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($"MEAS:POW:AC?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0; return ExtractDouble(resp);
}
#endregion
#region 6. LIST (LIST )
/// <summary>
/// 设定列表功能的模态 (SOUR:LIST:COUP)
/// </summary>
/// <param name="coupling">耦合模态: ALL | NONE</param>
public virtual async Task Async(string coupling, CancellationToken ct = default)
{
await SendAsync($"SOUR:LIST:COUP {coupling}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定列表功能的触发形态 (SOUR:LIST:TRIG)
/// </summary>
/// <param name="triggerMode">触发形态: AUTO | MANUAL | EXCITE</param>
public virtual async Task Async(string triggerMode, CancellationToken ct = default)
{
await SendAsync($"SOUR:LIST:TRIG {triggerMode}{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询列表功能的有效序列数 (SOUR:LIST:POIN?)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>有效序列数 (0 ~ 100)</returns>
public virtual async Task<int> Async(CancellationToken ct = default)
{
string resp = await WriteReadAsync($"SOUR:LIST:POIN?{ScpiDelimiter}", ScpiDelimiter, ct);
return int.TryParse(resp.Trim(), out int val) ? val : 0;
}
/// <summary>
/// 设定列表执行完成前的执行次数 (SOUR:LIST:COUN)
/// </summary>
/// <param name="count">执行次数 (0 ~ 65535)</param>
public virtual async Task Async(int count, CancellationToken ct = default)
{
await SendAsync($"SOUR:LIST:COUN {count}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定列表点的静止时间顺序 (SOUR:LIST:DWEL)
/// </summary>
/// <param name="dwellTimes">静止时间序列 (单位: ms,每个值 0 ~ 99999999.9)</param>
public virtual async Task Async(double[] dwellTimes, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(dwellTimes, v => v.ToString("0.#", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:DWEL {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定波形缓冲区列表点数的顺序 (SOUR:LIST:SHAP)
/// </summary>
/// <param name="shapes">波形缓冲区顺序,每个元素为 A 或 B</param>
public virtual async Task Async(string[] shapes, CancellationToken ct = default)
{
string valStr = string.Join(",", shapes);
await SendAsync($"SOUR:LIST:SHAP {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定列表的时间基础 (SOUR:LIST:BASE)
/// </summary>
/// <param name="timeBase">时间基础: TIME | CYCLE</param>
public virtual async Task Async(string timeBase, CancellationToken ct = default)
{
await SendAsync($"SOUR:LIST:BASE {timeBase}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定 AC 起始电压列表点数的顺序 (SOUR:LIST:VOLT:AC:STAR)
/// </summary>
/// <param name="voltages">AC 起始电压序列 (单位: V,每个值 0.0 ~ 300.0)</param>
public virtual async Task AC起始电压顺序Async(double[] voltages, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(voltages, v => v.ToString("0.###", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:VOLT:AC:STAR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定 AC 结束电压列表点数的顺序 (SOUR:LIST:VOLT:AC:END)
/// </summary>
/// <param name="voltages">AC 结束电压序列 (单位: V,每个值 0.0 ~ 300.0)</param>
public virtual async Task AC结束电压顺序Async(double[] voltages, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(voltages, v => v.ToString("0.###", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:VOLT:AC:END {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定 DC 起始电压列表点数的顺序 (SOUR:LIST:VOLT:DC:STAR)
/// </summary>
/// <param name="voltages">DC 起始电压序列 (单位: V,每个值 -424.2 ~ 414.2)</param>
public virtual async Task DC起始电压顺序Async(double[] voltages, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(voltages, v => v.ToString("0.###", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:VOLT:DC:STAR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定 DC 结束电压列表点数的顺序 (SOUR:LIST:VOLT:DC:END)
/// </summary>
/// <param name="voltages">DC 结束电压序列 (单位: V,每个值 -424.2 ~ 414.2)</param>
public virtual async Task DC结束电压顺序Async(double[] voltages, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(voltages, v => v.ToString("0.###", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:VOLT:DC:END {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定起始频率列表点数的顺序 (SOUR:LIST:FREQ:STAR)
/// </summary>
/// <param name="frequencies">起始频率序列 (单位: Hz,每个值 15.00 ~ 100.00)</param>
public virtual async Task Async(double[] frequencies, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(frequencies, v => v.ToString("0.##", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:FREQ:STAR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定结束频率列表点数的顺序 (SOUR:LIST:FREQ:END)
/// </summary>
/// <param name="frequencies">结束频率序列 (单位: Hz,每个值 15.00 ~ 100.00)</param>
public virtual async Task Async(double[] frequencies, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(frequencies, v => v.ToString("0.##", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:FREQ:END {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定相位角度列表点数的顺序 (SOUR:LIST:DEGR)
/// </summary>
/// <param name="degrees">相位角度序列 (单位: °,每个值 0.0 ~ 359.9)</param>
public virtual async Task Async(double[] degrees, CancellationToken ct = default)
{
string valStr = string.Join(",", Array.ConvertAll(degrees, v => v.ToString("0.#", CultureInfo.InvariantCulture)));
await SendAsync($"SOUR:LIST:DEGR {valStr}{ScpiDelimiter}", ct);
}
/// <summary>
/// 设定操作模态 (OUTP:MODE)
/// </summary>
/// <param name="mode">操作模态: FIXED | LIST | PULSE | STEP | SYNTH | INTERHAR</param>
public virtual async Task Async(string mode, CancellationToken ct = default)
{
await SendAsync($"OUTP:MODE {mode}{ScpiDelimiter}", ct);
}
/// <summary>
/// 在 LIST 模态下设定执行状态 (TRIG)
/// <para>需先通过 <see cref="设置操作模态Async"/> 将 OUTP:MODE 设为 LIST</para>
/// </summary>
/// <param name="isOn">true: 开始执行 LIST, false: 停止 LIST</param>
public virtual async Task Async(bool isOn, CancellationToken ct = default)
{
string state = isOn ? "ON" : "OFF";
await SendAsync($"TRIG {state}{ScpiDelimiter}", ct);
} }
#endregion #endregion
+137 -19
View File
@@ -3,6 +3,7 @@ using DeviceCommand.Base;
using Model.Models; using Model.Models;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -45,33 +46,74 @@ namespace DeviceCommand.Devices
#endregion #endregion
/// <summary>
/// 信号发生器 (DG1000Z 系列) 一拖三,支持波形输出与频率计测量
/// </summary>
[ACPCommand] [ACPCommand]
public class DG1000Z : Tcp public class DG1000Z : Tcp
{ {
// 使用换行符 \n (ASCII 0x0A) 作为 SCPI 结束符 // 使用换行符 \n (ASCII 0x0A) 作为 SCPI 结束符
private const string ScpiDelimiter = "\n"; private const string ScpiDelimiter = "\n";
/// <summary>
/// 构造函数:传入 <see cref="TcpConfig"/> 一次性初始化信号发生器通信参数
/// </summary>
public DG1000Z(TcpConfig config) : base(config) public DG1000Z(TcpConfig config) : base(config)
{ {
} }
/// <summary>
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary>
/// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return 0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
}
#region 1. IEEE 488.2 #region 1. IEEE 488.2
/// <summary>
/// 清除标准事件状态寄存器和错误队列 (*CLS)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
await SendAsync($"*CLS{ScpiDelimiter}", ct); await SendAsync($"*CLS{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询设备识别信息 (*IDN? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>设备标识字符串 (制造商, 型号, 序列号, 固件版本)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary>
/// 重设仪器为初始状态 (*RST)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
await SendAsync($"*RST{ScpiDelimiter}", ct); await SendAsync($"*RST{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询仪器支持的 SCPI 版本 (SYSTem:VERSion? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>SCPI 版本号字符串</returns>
public virtual async Task<string> SCPI版本(CancellationToken ct = default) public virtual async Task<string> SCPI版本(CancellationToken ct = default)
{ {
return await WriteReadAsync($":SYSTem:VERSion?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($":SYSTem:VERSion?{ScpiDelimiter}", ScpiDelimiter, ct);
@@ -82,16 +124,23 @@ namespace DeviceCommand.Devices
#region 2. (CH1 / CH2) #region 2. (CH1 / CH2)
/// <summary> /// <summary>
/// 打开或关闭指定通道的输出 /// 打开或关闭指定通道的输出 (OUTPut 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param> /// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="开启">true为开启,false为关闭</param> /// <param name="开启">true为开启,false为关闭</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, bool = true, CancellationToken ct = default) public virtual async Task (int = 1, bool = true, CancellationToken ct = default)
{ {
string = ? "ON" : "OFF"; string = ? "ON" : "OFF";
await SendAsync($":OUTPut{通道号}:STATe {参数}{ScpiDelimiter}", ct); await SendAsync($":OUTPut{通道号}:STATe {参数}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询指定通道的输出开关状态 (OUTPut:STATe? 指令)
/// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="ct">取消令牌</param>
/// <returns>通道输出状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (int = 1, CancellationToken ct = default) public virtual async Task<string> (int = 1, CancellationToken ct = default)
{ {
return await WriteReadAsync($":OUTPut{通道号}:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($":OUTPut{通道号}:STATe?{ScpiDelimiter}", ScpiDelimiter, ct);
@@ -104,51 +153,84 @@ namespace DeviceCommand.Devices
/// <summary> /// <summary>
/// 设置指定通道的波形类型 (如正弦波、方波等) /// 设置指定通道的波形类型 (如正弦波、方波等)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="波形">波形类型枚举,默认正弦波</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, SignalWaveform = SignalWaveform.SINusoid, CancellationToken ct = default) public virtual async Task (int = 1, SignalWaveform = SignalWaveform.SINusoid, CancellationToken ct = default)
{ {
await SendAsync($":SOURce{通道号}:FUNCtion:SHAPe {波形}{ScpiDelimiter}", ct); await SendAsync($":SOURce{通道号}:FUNCtion:SHAPe {波形}{ScpiDelimiter}", ct);
} }
/// <summary> /// <summary>
/// 设置指定通道的输出频率 (单位: Hz) /// 设置指定通道的输出频率 (SOURce:FREQuency 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="频率Hz">输出频率值 (单位: Hz),默认 1000</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, double Hz = 1000, CancellationToken ct = default) public virtual async Task (int = 1, double Hz = 1000, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FREQuency {1:F2}{2}", , Hz, ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FREQuency {1:F2}{2}", , Hz, ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
public virtual async Task<string> (int = 1, CancellationToken ct = default) /// <summary>
/// 查询指定通道的当前输出频率 (SOURce:FREQuency? 指令)
/// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="ct">取消令牌</param>
/// <returns>输出频率值 (单位: Hz,保留两位小数)</returns>
public virtual async Task<double> (int = 1, CancellationToken ct = default)
{ {
return await WriteReadAsync($":SOURce{通道号}:FREQuency?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($":SOURce{通道号}:FREQuency?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
} }
/// <summary> /// <summary>
/// 设置指定通道的输出幅度 (单位: Vpp,峰峰值) /// 设置指定通道的输出幅度 (SOURce:VOLTage:AMPLitude 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="幅度Vpp">输出幅度 (单位: Vpp 峰峰值),默认 5</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, double Vpp = 5, CancellationToken ct = default) public virtual async Task (int = 1, double Vpp = 5, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:VOLTage:AMPLitude {1:F2}{2}", , Vpp, ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:VOLTage:AMPLitude {1:F2}{2}", , Vpp, ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
public virtual async Task<string> (int = 1, CancellationToken ct = default) /// <summary>
/// 查询指定通道的当前输出幅度 (SOURce:VOLTage:AMPLitude? 指令)
/// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="ct">取消令牌</param>
/// <returns>输出幅度 (单位: Vpp,保留两位小数)</returns>
public virtual async Task<double> (int = 1, CancellationToken ct = default)
{ {
return await WriteReadAsync($":SOURce{通道号}:VOLTage:AMPLitude?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($":SOURce{通道号}:VOLTage:AMPLitude?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
} }
/// <summary> /// <summary>
/// 设置指定通道的直流偏移电压 (单位: Vdc) /// 设置指定通道的直流偏移电压 (SOURce:VOLTage:OFFSet 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="偏移Vdc">直流偏移电压 (单位: Vdc),默认 0</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, double Vdc = 0, CancellationToken ct = default) public virtual async Task (int = 1, double Vdc = 0, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:VOLTage:OFFSet {1:F2}{2}", , Vdc, ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:VOLTage:OFFSet {1:F2}{2}", , Vdc, ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
public virtual async Task<string> (int = 1, CancellationToken ct = default) /// <summary>
/// 查询指定通道的当前直流偏移电压 (SOURce:VOLTage:OFFSet? 指令)
/// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="ct">取消令牌</param>
/// <returns>直流偏移电压 (单位: Vdc,保留两位小数)</returns>
public virtual async Task<double> (int = 1, CancellationToken ct = default)
{ {
return await WriteReadAsync($":SOURce{通道号}:VOLTage:OFFSet?{ScpiDelimiter}", ScpiDelimiter, ct); string resp = await WriteReadAsync($":SOURce{通道号}:VOLTage:OFFSet?{ScpiDelimiter}", ScpiDelimiter, ct);
return ExtractDouble(resp);
} }
#endregion #endregion
@@ -156,8 +238,11 @@ namespace DeviceCommand.Devices
#region 4. / (CP信号模拟) #region 4. / (CP信号模拟)
/// <summary> /// <summary>
/// 设置方波信号的占空比 (0.01% ~ 99.99%)[cite: 1] /// 设置方波信号的占空比 (0.01% ~ 99.99%)
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="占空比">方波占空比百分比,默认 50.0</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, double = 50.0, CancellationToken ct = default) public virtual async Task (int = 1, double = 50.0, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FUNCtion:SQUare:DCYCle {1:F2}{2}", , , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FUNCtion:SQUare:DCYCle {1:F2}{2}", , , ScpiDelimiter);
@@ -165,9 +250,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置脉冲波信号的占空比 (0.01% ~ 99.99%)[cite: 1] /// 设置脉冲波信号的占空比 (0.01% ~ 99.99%)
/// 注意:使用此命令前必须先设置为 PULSe 波形类型 /// 注意:使用此命令前必须先设置为 PULSe 波形类型
/// </summary> /// </summary>
/// <param name="通道号">通道 1 或 2,默认 1</param>
/// <param name="占空比">脉冲波占空比百分比,默认 50.0</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int = 1, double = 50.0, CancellationToken ct = default) public virtual async Task (int = 1, double = 50.0, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FUNCtion:PULSe:DCYCle {1:F2}{2}", , , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce{0}:FUNCtion:PULSe:DCYCle {1:F2}{2}", , , ScpiDelimiter);
@@ -179,47 +267,68 @@ namespace DeviceCommand.Devices
#region 5. () #region 5. ()
/// <summary> /// <summary>
/// 打开或关闭内置频率计功能 /// 打开或关闭内置频率计功能 (COUNter:STATe 指令)
/// </summary> /// </summary>
/// <param name="开启">true: 开启频率计, false: 关闭频率计</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (bool = true, CancellationToken ct = default) public virtual async Task (bool = true, CancellationToken ct = default)
{ {
string = ? "ON" : "OFF"; string = ? "ON" : "OFF";
await SendAsync($":COUNter:STATe {参数}{ScpiDelimiter}", ct); await SendAsync($":COUNter:STATe {参数}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询内置频率计的开关状态 (COUNter:STATe? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>频率计开关状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync($":COUNter:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($":COUNter:STATe?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary> /// <summary>
/// 设置频率计输入信号的耦合方式 /// 设置频率计输入信号的耦合方式 (COUNter:COUPling 指令)
/// </summary> /// </summary>
/// <param name="耦合">耦合方式枚举 (AC 交流 / DC 直流),默认 AC</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (CounterCoupling = CounterCoupling.AC, CancellationToken ct = default) public virtual async Task (CounterCoupling = CounterCoupling.AC, CancellationToken ct = default)
{ {
await SendAsync($":COUNter:COUPling {耦合}{ScpiDelimiter}", ct); await SendAsync($":COUNter:COUPling {耦合}{ScpiDelimiter}", ct);
} }
/// <summary> /// <summary>
/// 设置频率计的触发电平 (单位: V) /// 设置频率计的触发电平 (COUNter:LEVel 指令)
/// </summary> /// </summary>
/// <param name="电平V">触发电平值 (单位: V),默认 0.1</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (double V = 0.1, CancellationToken ct = default) public virtual async Task (double V = 0.1, CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, ":COUNter:LEVel {0:F2}{1}", V, ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, ":COUNter:LEVel {0:F2}{1}", V, ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
/// <summary>
/// 查询频率计测量值 (COUNter:MEASure? 指令)。
/// 响应格式为 "频率, 周期, 占空比, 正脉宽, 负脉宽",此方法提取第一项频率值
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>频率计测得的频率值 (单位: Hz,保留两位小数)</returns>
[Monitorable("信号发生器频率计测量值")] [Monitorable("信号发生器频率计测量值")]
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<double> (CancellationToken ct = default)
{ {
// 返回格式: 频率, 周期, 占空比, 正脉宽, 负脉宽 string resp = await WriteReadAsync($":COUNter:MEASure?{ScpiDelimiter}", ScpiDelimiter, ct);
return await WriteReadAsync($":COUNter:MEASure?{ScpiDelimiter}", ScpiDelimiter, ct); return ExtractDouble(resp);
} }
#endregion #endregion
#region 6. #region 6.
/// <summary>
/// 切换远程控制模式。信号发生器通过 LAN/USB 通信即自动进入远程模式
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
// 信号发生器通常通过 LAN/USB 和 VISA 通信即自动进入远程控制, // 信号发生器通常通过 LAN/USB 和 VISA 通信即自动进入远程控制,
@@ -227,12 +336,21 @@ namespace DeviceCommand.Devices
await Task.CompletedTask; await Task.CompletedTask;
} }
/// <summary>
/// 切换本地控制模式。通常通过前面板按键退出远程
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
// 通常通过前面板按键退出远程。不过可提供 SCPI 关闭屏幕保护等。 // 通常通过前面板按键退出远程。不过可提供 SCPI 关闭屏幕保护等。
await Task.CompletedTask; await Task.CompletedTask;
} }
/// <summary>
/// 查询系统错误信息 (SYSTem:ERRor? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>错误代码和描述信息</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync($":SYSTem:ERRor?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($":SYSTem:ERRor?{ScpiDelimiter}", ScpiDelimiter, ct);
@@ -240,4 +358,4 @@ namespace DeviceCommand.Devices
#endregion #endregion
} }
} }
+34 -3
View File
@@ -12,22 +12,39 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary> /// <summary>
/// IO板卡 一对一由于板卡映射不完全一样采用IOGroup /// IO 继电器板卡一对一控制),基于 Modbus TCP 协议通过线圈(Coil)读写控制继电器输出。
/// 由于多块板卡的点位映射不完全一致,实际业务中使用 <see cref="IOBoardGroup"/> 统一调度三块板卡。
/// </summary> /// </summary>
//[ACPCommand] //[ACPCommand]
public class IOBoard : ModbusTcp public class IOBoard : ModbusTcp
{ {
/// <summary>
/// 构造函数,初始化 Modbus TCP 连接配置。
/// </summary>
/// <param name="config">TCP 连接配置(IP、端口等)</param>
public IOBoard(TcpConfig config) : base(config) public IOBoard(TcpConfig config) : base(config)
{ {
} }
/// <summary>
/// 写入单个输出开关(线圈)状态。
/// </summary>
/// <param name="站号">Modbus 从站站号</param>
/// <param name="开始地址">线圈地址(0 起始)</param>
/// <param name="data">开关状态(true: 吸合/开启, false: 断开/关闭)</param>
public async Task (byte , ushort , bool data) public async Task (byte , ushort , bool data)
{ {
await Modbus.WriteSingleCoilAsync(, , data); await Modbus.WriteSingleCoilAsync(, , data);
} }
/// <summary>
/// 批量写入输出开关状态。先读出全部 16 个线圈当前状态,
/// 在内存中修改指定偏移段的值后再整体写回,避免影响其他点位。
/// </summary>
/// <param name="站号">Modbus 从站站号</param>
/// <param name="开始地址">起始线圈地址(0 起始)</param>
/// <param name="datas">要写入的开关状态数组,按顺序从开始地址写入</param>
public async Task (byte , ushort , bool[] datas) public async Task (byte , ushort , bool[] datas)
{ {
var = await Modbus.ReadCoilsAsync(1, 0, 16); var = await Modbus.ReadCoilsAsync(1, 0, 16);
@@ -40,10 +57,24 @@ namespace DeviceCommand.Devices
} }
await Modbus.WriteMultipleCoilsAsync(, 0, ); await Modbus.WriteMultipleCoilsAsync(, 0, );
} }
/// <summary>
/// 读取单个输出开关(线圈)状态。
/// </summary>
/// <param name="站号">Modbus 从站站号</param>
/// <param name="开始地址">线圈地址(0 起始)</param>
public async Task (byte , ushort ) public async Task (byte , ushort )
{ {
await Modbus.ReadCoilsAsync(, , 1); await Modbus.ReadCoilsAsync(, , 1);
} }
/// <summary>
/// 批量读取输出开关(线圈)状态。
/// </summary>
/// <param name="站号">Modbus 从站站号</param>
/// <param name="开始地址">起始线圈地址(0 起始)</param>
/// <param name="数量">要读取的线圈数量</param>
/// <returns>线圈状态数组,true 表示吸合/开启</returns>
public async Task<bool[]> (byte , ushort ,ushort ) public async Task<bool[]> (byte , ushort ,ushort )
{ {
return await Modbus.ReadCoilsAsync(, , ); return await Modbus.ReadCoilsAsync(, , );
+25 -37
View File
@@ -48,6 +48,11 @@ namespace DeviceCommand.Devices
} }
} }
/// <summary>
/// IO 板卡组(三工位统一调度),封装 3 块 IO 继电器板卡的访问。
/// 根据 ACP 硬件接线图的点位映射字典,按台架序号路由到对应板卡的线圈地址,
/// 并内置充电/放电互锁保护,防止同时吸合造成短路。
/// </summary>
[ACPCommand] [ACPCommand]
public class IOBoardGroup public class IOBoardGroup
{ {
@@ -121,6 +126,13 @@ namespace DeviceCommand.Devices
}} }}
}; };
/// <summary>
/// 构造函数,注入三个工位的 IO 板卡实例。
/// </summary>
/// <param name="board1">1# 工位 IO 板卡实例</param>
/// <param name="board2">2# 工位 IO 板卡实例</param>
/// <param name="board3">3# 工位 IO 板卡实例</param>
/// <exception cref="ArgumentNullException">任一块板卡实例为空时抛出</exception>
// 修改构造函数以支持 3 个板块 // 修改构造函数以支持 3 个板块
public IOBoardGroup(IOBoard board1, IOBoard board2, IOBoard board3) public IOBoardGroup(IOBoard board1, IOBoard board2, IOBoard board3)
{ {
@@ -168,49 +180,25 @@ namespace DeviceCommand.Devices
await .(_slaveAddress, io信息.Address, ); await .(_slaveAddress, io信息.Address, );
} }
// =================================================================================
// 以下为新增的原生命令透传接口,使用台架序号自动路由至对应 IOBoard
// =================================================================================
/// <summary> /// <summary>
/// 针对指定台架直接写入单个输出开关 /// 向指定板卡批量写入输出开关状态,直接透传到对应板卡的批量写线圈操作。
/// </summary> /// </summary>
public async Task (int , byte , ushort , bool data) /// <param name="板卡序号">板卡序号 (1 - 3)</param>
/// <param name="站号">Modbus 从站站号</param>
/// <param name="开始地址">线圈起始地址 (0-15)</param>
/// <param name="数据">开关状态数组 (true: 开启吸合, false: 关闭断开),例如 [true, true, false]</param>
/// <param name="取消令牌">异步取消令牌</param>
public async Task (int , byte , ushort , bool[] , CancellationToken = default)
{ {
IOBoard targetBoard = GetBoardInstance(); await GetBoardInstance().(, , );
await targetBoard.(, , data);
} }
/// <summary> /// <summary>
/// 针对指定台架直接批量写入输出开关 /// 内部路由辅助方法:根据板卡序号安全获取对应的 IO 板卡实例。
/// </summary>
public async Task (int , byte , ushort , bool[] datas)
{
IOBoard targetBoard = GetBoardInstance();
await targetBoard.(, , datas);
}
/// <summary>
/// 针对指定台架直接读取单个输出开关状态
/// </summary>
public async Task (int , byte , ushort )
{
IOBoard targetBoard = GetBoardInstance();
await targetBoard.(, );
}
/// <summary>
/// 针对指定台架直接批量读取输出开关状态
/// </summary>
public async Task<bool[]> (int , byte , ushort , ushort )
{
IOBoard targetBoard = GetBoardInstance();
return await targetBoard.(, , );
}
/// <summary>
/// 内部路由辅助方法:根据板卡/台架索引安全获取当前连接的实例对象
/// </summary> /// </summary>
/// <param name="moduleIndex">板卡序号 (1 - 3)</param>
/// <returns>对应的 IO 板卡实例</returns>
/// <exception cref="ArgumentOutOfRangeException">板卡序号不在 1-3 范围内时抛出</exception>
/// <exception cref="InvalidOperationException">板卡实例未初始化时抛出</exception>
private IOBoard GetBoardInstance(int moduleIndex) private IOBoard GetBoardInstance(int moduleIndex)
{ {
IOBoard board = moduleIndex switch IOBoard board = moduleIndex switch
+23 -4
View File
@@ -133,6 +133,10 @@ namespace DeviceCommand.Devices
throw new Exception("IT6720 切换控制模式时通讯校验失败。"); throw new Exception("IT6720 切换控制模式时通讯校验失败。");
} }
/// <summary>
/// 切换为本地(面板)控制模式,与远程控制互斥
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
await (false, ct); await (false, ct);
@@ -150,6 +154,11 @@ namespace DeviceCommand.Devices
throw new Exception("IT6720 设置输出时通讯校验失败。"); throw new Exception("IT6720 设置输出时通讯校验失败。");
} }
/// <summary>
/// 查询电源输出开关状态(由实时测量值的状态字节判断)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>输出开关状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
var (_, _, status) = await (ct); var (_, _, status) = await (ct);
@@ -230,18 +239,28 @@ namespace DeviceCommand.Devices
return (actualCurrent, actualVoltage, statusByte); return (actualCurrent, actualVoltage, statusByte);
} }
/// <summary>
/// 查询电源的实时实际输出电压 (MEASure 相关寄存器)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>实时电压值 (单位: V,保留两位小数)</returns>
[Monitorable("IT6720 实际电压")] [Monitorable("IT6720 实际电压")]
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<double> (CancellationToken ct = default)
{ {
var (_, voltage, _) = await (ct); var (_, voltage, _) = await (ct);
return voltage.ToString("F2", CultureInfo.InvariantCulture); return Math.Round(voltage, 2);
} }
/// <summary>
/// 查询电源的实时实际输出电流 (MEASure 相关寄存器)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>实时电流值 (单位: A,保留两位小数)</returns>
[Monitorable("IT6720 实际电流")] [Monitorable("IT6720 实际电流")]
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<double> (CancellationToken ct = default)
{ {
var (current, _, _) = await (ct); var (current, _, _) = await (ct);
return current.ToString("F3", CultureInfo.InvariantCulture); return Math.Round(current, 2);
} }
/// <summary> /// <summary>
+93 -29
View File
@@ -8,16 +8,22 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary> /// <summary>
/// 水冷机 Modbus TCP 驱动 (支持3回路独立控制) /// MCc30W 水冷机(一拖三控制),基于 Modbus TCP 协议(MCx ModbusTCP)通信。
/// 通讯协议: MCx ModbusTCP /// 支持 3 回路独立控制:出液温度/流量/压力的目标设定与实时读取、回路启停、
/// 预控温、控流/控压模式切换、自动回液、报警复位/消音及状态回读。
/// 多寄存器数值采用 IEEE754 浮点大端序编码(2 个 16 位寄存器)。
/// </summary> /// </summary>
[ACPCommand] [ACPCommand]
public class : ModbusTcp public class MCc30W : ModbusTcp
{ {
// 默认配置 (可从 config 或 TcpConfig 注入) // 默认配置 (可从 config 或 TcpConfig 注入)
private readonly byte _slaveId = 0x01; // 单元标识符 private readonly byte _slaveId = 0x01; // 单元标识符
public (TcpConfig config) : base(config) /// <summary>
/// 构造函数,初始化 Modbus TCP 连接配置。
/// </summary>
/// <param name="config">TCP 连接配置(IP、端口等)</param>
public MCc30W(TcpConfig config) : base(config)
{ {
} }
@@ -81,8 +87,11 @@ namespace DeviceCommand.Devices
#region 2. API ( 3 ) #region 2. API ( 3 )
/// <summary> /// <summary>
/// 设置指定回路的出液目标温度 (℃) /// 设置指定回路的出液目标温度,写入对应回路的保持寄存器。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="temperature">出液目标温度,单位 ℃</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(int loop, float temperature, CancellationToken ct = default) public virtual async Task Async(int loop, float temperature, CancellationToken ct = default)
{ {
ushort address = (ushort)(0x0000 + (loop - 1) * 2); ushort address = (ushort)(0x0000 + (loop - 1) * 2);
@@ -91,8 +100,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置指定回路的出液目标流量 (L/min) /// 设置指定回路的出液目标流量,写入对应回路的保持寄存器。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="flow">出液目标流量,单位 L/min</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(int loop, float flow, CancellationToken ct = default) public virtual async Task Async(int loop, float flow, CancellationToken ct = default)
{ {
ushort address = (ushort)(0x0008 + (loop - 1) * 2); ushort address = (ushort)(0x0008 + (loop - 1) * 2);
@@ -101,8 +113,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置指定回路的出液目标压力 (kPa) /// 设置指定回路的出液目标压力,写入对应回路的保持寄存器。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="pressure">出液目标压力,单位 kPa</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(int loop, float pressure, CancellationToken ct = default) public virtual async Task Async(int loop, float pressure, CancellationToken ct = default)
{ {
ushort address = (ushort)(0x000E + (loop - 1) * 2); ushort address = (ushort)(0x000E + (loop - 1) * 2);
@@ -111,8 +126,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的当前出液温度 (℃) /// 读取指定回路的当前出液温度
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>当前出液温度,单位 ℃</returns>
[Monitorable("水冷机出液当前温度")] [Monitorable("水冷机出液当前温度")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -122,9 +140,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的当前出液流量 (L/min) /// 读取指定回路的当前出液流量
/// 注意:协议中三通道流量地址间隔为 0x08 /// 注意:协议中三通道流量地址间隔为 0x08
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>当前出液流量,单位 L/min</returns>
[Monitorable("水冷机出液当前流量")] [Monitorable("水冷机出液当前流量")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -134,9 +155,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的当前出液压力 (kPa) /// 读取指定回路的当前出液压力
/// 注意:协议中三通道压力地址间隔为 0x08 /// 注意:协议中三通道压力地址间隔为 0x08
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>当前出液压力,单位 kPa</returns>
[Monitorable("水冷机出液当前压力")] [Monitorable("水冷机出液当前压力")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -146,8 +170,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的当前回液压力 (kPa) (0x0022 + (loop-1)*8) /// 读取指定回路的当前回液压力(寄存器地址 0x0022 + (loop-1)*8)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>当前回液压力,单位 kPa</returns>
[Monitorable("水冷机回液当前压力")] [Monitorable("水冷机回液当前压力")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -157,8 +184,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的当前回液温度 (℃) (0x0024 + (loop-1)*8) /// 读取指定回路的当前回液温度(寄存器地址 0x0024 + (loop-1)*8)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>当前回液温度,单位 ℃</returns>
[Monitorable("水冷机回液当前温度")] [Monitorable("水冷机回液当前温度")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -168,8 +198,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的外部温度反馈 (℃) (0x0036 + (loop-1)*2) /// 读取指定回路的外部温度反馈(寄存器地址 0x0036 + (loop-1)*2)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>外部温度反馈值,单位 ℃</returns>
[Monitorable("水冷机外部温度反馈")] [Monitorable("水冷机外部温度反馈")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -179,8 +212,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取指定回路的温变速率设定 (℃/min) (0x0006 + (loop-1)*2) /// 读取指定回路的温变速率设定值(寄存器地址 0x0006 + (loop-1)*2)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3)</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>温变速率设定值,单位 ℃/min</returns>
[Monitorable("水冷机温变速率")] [Monitorable("水冷机温变速率")]
public virtual async Task<float> Async(int loop, CancellationToken ct = default) public virtual async Task<float> Async(int loop, CancellationToken ct = default)
{ {
@@ -190,8 +226,10 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取回抽2流量目标值 (L/min) (0x0016) /// 读取回抽2流量目标值(固定寄存器地址 0x0016,不区分回路)。
/// </summary> /// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>回抽2流量目标值,单位 L/min</returns>
[Monitorable("水冷机回抽2流量目标值")] [Monitorable("水冷机回抽2流量目标值")]
public virtual async Task<float> 2Async(CancellationToken ct = default) public virtual async Task<float> 2Async(CancellationToken ct = default)
{ {
@@ -223,8 +261,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 启动/停止 指定回路 (Bit 01, 02, 03) /// 启动/停止指定回路(操作系统状态控制寄存器 0x0014 的 Bit 01/02/03)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3),分别对应 Bit 1/2/3</param>
/// <param name="enable">true: 启动回路, false: 停止回路</param>
/// <param name="ct">异步取消令牌</param>
/// <exception cref="ArgumentOutOfRangeException">回路号不在 1-3 范围内时抛出</exception>
public virtual async Task Async(int loop, bool enable, CancellationToken ct = default) public virtual async Task Async(int loop, bool enable, CancellationToken ct = default)
{ {
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3"); if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
@@ -232,8 +274,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 启动/停止 指定回路 预控温 (Bit 10, 11, 12) /// 启动/停止指定回路预控温(操作系统状态控制寄存器 0x0014 的 Bit 10/11/12)。
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3),分别对应 Bit 10/11/12</param>
/// <param name="enable">true: 开启预控温, false: 关闭预控温</param>
/// <param name="ct">异步取消令牌</param>
/// <exception cref="ArgumentOutOfRangeException">回路号不在 1-3 范围内时抛出</exception>
public virtual async Task Async(int loop, bool enable, CancellationToken ct = default) public virtual async Task Async(int loop, bool enable, CancellationToken ct = default)
{ {
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3"); if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
@@ -241,9 +287,12 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置指定回路 流量/压力 控制模式 (Bit 04, 05, 06) /// 设置指定回路流量/压力控制模式(操作系统状态控制寄存器 0x0014 的 Bit 04/05/06)。
/// true=控压, false=控流
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3),分别对应 Bit 4/5/6</param>
/// <param name="isPressureControl">true: 控压模式, false: 控流模式</param>
/// <param name="ct">异步取消令牌</param>
/// <exception cref="ArgumentOutOfRangeException">回路号不在 1-3 范围内时抛出</exception>
public virtual async Task Async(int loop, bool isPressureControl, CancellationToken ct = default) public virtual async Task Async(int loop, bool isPressureControl, CancellationToken ct = default)
{ {
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3"); if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
@@ -251,25 +300,32 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 报警复位 (Bit 08) /// 报警复位(操作系统状态控制寄存器 0x0014 的 Bit 08),用于清除水冷机报警状态。
/// </summary> /// </summary>
/// <param name="enable">true: 置位报警复位位, false: 清除该位</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(bool enable, CancellationToken ct = default) public virtual async Task Async(bool enable, CancellationToken ct = default)
{ {
await SetControlBit(8, enable, ct); await SetControlBit(8, enable, ct);
} }
/// <summary> /// <summary>
/// 报警消音 (Bit 09) /// 报警消音(操作系统状态控制寄存器 0x0014 的 Bit 09),用于关闭报警蜂鸣。
/// </summary> /// </summary>
/// <param name="enable">true: 置位消音位, false: 清除该位</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task Async(bool enable, CancellationToken ct = default) public virtual async Task Async(bool enable, CancellationToken ct = default)
{ {
await SetControlBit(9, enable, ct); await SetControlBit(9, enable, ct);
} }
/// <summary> /// <summary>
/// 设置指定回路 自动回液开关 (寄存器 0x0015, 低位部分) /// 设置指定回路自动回液开关(操作寄存器 0x0015 的 Bit 13/14/15)。
/// 此方法操作的是 0x0015 寄存器的低 16 位
/// </summary> /// </summary>
/// <param name="loop">回路编号 (1-3),分别对应 Bit 13/14/15</param>
/// <param name="enable">true: 开启自动回液, false: 关闭自动回液</param>
/// <param name="ct">异步取消令牌</param>
/// <exception cref="ArgumentOutOfRangeException">回路号不在 1-3 范围内时抛出</exception>
public virtual async Task Async(int loop, bool enable, CancellationToken ct = default) public virtual async Task Async(int loop, bool enable, CancellationToken ct = default)
{ {
if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3"); if (loop < 1 || loop > 3) throw new ArgumentOutOfRangeException(nameof(loop), "回路号必须为1, 2, 3");
@@ -293,8 +349,10 @@ namespace DeviceCommand.Devices
#region 4. () #region 4. ()
/// <summary> /// <summary>
/// 读取心跳帧 (0x0044),用于判断通讯是否正常 /// 读取心跳帧(寄存器 0x0044),心跳值持续变化,用于判断通讯是否正常
/// </summary> /// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>32 位心跳计数值</returns>
[Monitorable("水冷机心跳帧")] [Monitorable("水冷机心跳帧")]
public virtual async Task<uint> Async(CancellationToken ct = default) public virtual async Task<uint> Async(CancellationToken ct = default)
{ {
@@ -303,8 +361,10 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取运行状态反馈 (0x0046),含各回路完成状态 (Sheet5) /// 读取运行状态反馈(寄存器 0x0046,含各回路完成状态位(协议 Sheet5)。
/// </summary> /// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>32 位运行状态位图</returns>
[Monitorable("水冷机运行状态反馈")] [Monitorable("水冷机运行状态反馈")]
public virtual async Task<uint> Async(CancellationToken ct = default) public virtual async Task<uint> Async(CancellationToken ct = default)
{ {
@@ -313,8 +373,10 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取报警信息1 (0x003C) /// 读取报警信息1(寄存器 0x003C),每一位对应一种报警类型。
/// </summary> /// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>32 位报警信息位图,0 表示无报警</returns>
[Monitorable("水冷机报警信息1")] [Monitorable("水冷机报警信息1")]
public virtual async Task<uint> 1Async(CancellationToken ct = default) public virtual async Task<uint> 1Async(CancellationToken ct = default)
{ {
@@ -323,8 +385,10 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 读取报警信息2 (0x003E) /// 读取报警信息2(寄存器 0x003E),每一位对应一种报警类型。
/// </summary> /// </summary>
/// <param name="ct">异步取消令牌</param>
/// <returns>32 位报警信息位图,0 表示无报警</returns>
[Monitorable("水冷机报警信息2")] [Monitorable("水冷机报警信息2")]
public virtual async Task<uint> 2Async(CancellationToken ct = default) public virtual async Task<uint> 2Async(CancellationToken ct = default)
{ {
+261 -33
View File
@@ -2,13 +2,16 @@
using Common.Attributes; using Common.Attributes;
using DeviceCommand.Base; using DeviceCommand.Base;
using Model.Models; using Model.Models;
using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DeviceCommand.Device namespace DeviceCommand.Device
{ {
/// <summary> /// <summary>
/// 功率分析仪(型号:HIOKI PW8001) 一拖三 /// 功率分析仪(型号:HIOKI PW8001) 一拖三,支持多通道电压/电流/功率及谐波测量
/// </summary> /// </summary>
[ACPCommand] [ACPCommand]
public class PW8001 : Tcp public class PW8001 : Tcp
@@ -25,23 +28,58 @@ namespace DeviceCommand.Device
/// </summary> /// </summary>
public string? SessionKey { get; set; } public string? SessionKey { get; set; }
/// <summary>
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary>
/// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return 0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
}
#region 1. IEEE 488.2 #region 1. IEEE 488.2
/// <summary>
/// 查询仪器识别信息 (*IDN? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>设备标识字符串 (制造商, 型号, 序列号, 固件版本)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync("*IDN?\r\n", "\n", ct); return await WriteReadAsync("*IDN?\r\n", "\n", ct);
} }
/// <summary>
/// 查询仪器已安装的硬件选件 (*OPT? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>硬件选件列表字符串</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync("*OPT?\r\n", "\n", ct); return await WriteReadAsync("*OPT?\r\n", "\n", ct);
} }
/// <summary>
/// 复位仪器到默认状态 (*RST 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
await SendAsync("*RST\r\n", ct); await SendAsync("*RST\r\n", ct);
} }
/// <summary>
/// 清除状态寄存器和错误队列 (*CLS 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
await SendAsync("*CLS\r\n", ct); await SendAsync("*CLS\r\n", ct);
@@ -51,66 +89,129 @@ namespace DeviceCommand.Device
#region 2. (System & Mode) #region 2. (System & Mode)
/// <summary>
/// 设置测试模式为 WIDE 宽带测量模式 (:MODE WIDE 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task _WIDE(CancellationToken ct = default) public virtual async Task _WIDE(CancellationToken ct = default)
{ {
await SendAsync(":MODE WIDE\r\n", ct); await SendAsync(":MODE WIDE\r\n", ct);
} }
/// <summary>
/// 设置测试模式为 IEC 标准测量模式 (:MODE IEC 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task _IEC(CancellationToken ct = default) public virtual async Task _IEC(CancellationToken ct = default)
{ {
await SendAsync(":MODE IEC\r\n", ct); await SendAsync(":MODE IEC\r\n", ct);
} }
/// <summary>
/// 查询当前测试模式 (:MODE? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>测试模式字符串 (WIDE / IEC)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":MODE?\r\n", "\n", ct); return await WriteReadAsync(":MODE?\r\n", "\n", ct);
} }
/// <summary>
/// 设置同步源 (:SYNC:SOURce 指令)
/// </summary>
/// <param name="源">同步源标识</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":SYNC:SOURce {源}\r\n", ct); await SendAsync($":SYNC:SOURce {源}\r\n", ct);
} }
/// <summary>
/// 查询当前同步源 (:SYNC:SOURce? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>同步源标识字符串</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":SYNC:SOURce?\r\n", "\n", ct); return await WriteReadAsync(":SYNC:SOURce?\r\n", "\n", ct);
} }
/// <summary>
/// 设置零点调整状态 (:ZERO 指令)
/// </summary>
/// <param name="状态">零点调整状态 (ON / OFF)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":ZERO {状态}\r\n", ct); await SendAsync($":ZERO {状态}\r\n", ct);
} }
/// <summary>
/// 查询零点调整状态 (:ZERO? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>零点调整状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":ZERO?\r\n", "\n", ct); return await WriteReadAsync(":ZERO?\r\n", "\n", ct);
} }
/// <summary>
/// 设置零点抑制状态 (:ZSP 指令)
/// </summary>
/// <param name="状态">零点抑制状态 (ON / OFF)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":ZSP {状态}\r\n", ct); await SendAsync($":ZSP {状态}\r\n", ct);
} }
/// <summary>
/// 查询零点抑制状态 (:ZSP? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>零点抑制状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":ZSP?\r\n", "\n", ct); return await WriteReadAsync(":ZSP?\r\n", "\n", ct);
} }
/// <summary>
/// 设置响应消息头开关 (:HEADer 指令)
/// </summary>
/// <param name="状态">消息头开关状态 (ON / OFF)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":HEADer {状态}\r\n", ct); await SendAsync($":HEADer {状态}\r\n", ct);
} }
/// <summary>
/// 查询响应消息头开关状态 (:HEADer? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>消息头开关状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":HEADer?\r\n", "\n", ct); return await WriteReadAsync(":HEADer?\r\n", "\n", ct);
} }
/// <summary>
/// 设置按键锁定状态 (:KLOCk 指令)
/// </summary>
/// <param name="状态">按键锁定状态 (ON / OFF)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":KLOCk {状态}\r\n", ct); await SendAsync($":KLOCk {状态}\r\n", ct);
} }
/// <summary>
/// 查询按键锁定状态 (:KLOCk? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>按键锁定状态字符串 (ON / OFF)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":KLOCk?\r\n", "\n", ct); return await WriteReadAsync(":KLOCk?\r\n", "\n", ct);
@@ -120,21 +221,43 @@ namespace DeviceCommand.Device
#region 3. (Integration) #region 3. (Integration)
/// <summary>
/// 设置积分控制模式 (:INTEG:CONTROL 指令)
/// </summary>
/// <param name="模式">积分控制模式</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($":INTEG:CONTROL {模式}\r\n", ct); await SendAsync($":INTEG:CONTROL {模式}\r\n", ct);
} }
/// <summary>
/// 查询积分控制模式 (:INTEG:CONTROL? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>积分控制模式字符串</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync(":INTEG:CONTROL?\r\n", "\n", ct); return await WriteReadAsync(":INTEG:CONTROL?\r\n", "\n", ct);
} }
/// <summary>
/// 设置指定通道的积分模式 (:INTEG:MODE 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="模式">积分模式</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , string , CancellationToken ct = default) public virtual async Task (string , string , CancellationToken ct = default)
{ {
await SendAsync($":INTEG:MODE{通道号} {模式}\r\n", ct); await SendAsync($":INTEG:MODE{通道号} {模式}\r\n", ct);
} }
/// <summary>
/// 查询指定通道的积分模式 (:INTEG:MODE? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>积分模式字符串</returns>
public virtual async Task<string> (string , CancellationToken ct = default) public virtual async Task<string> (string , CancellationToken ct = default)
{ {
return await WriteReadAsync($":INTEG:MODE{通道号}?\r\n", "\n", ct); return await WriteReadAsync($":INTEG:MODE{通道号}?\r\n", "\n", ct);
@@ -146,85 +269,188 @@ namespace DeviceCommand.Device
// ---------------- 基础测量(不含变比) ---------------- // ---------------- 基础测量(不含变比) ----------------
public virtual async Task<string> _不含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电压测量值(不含变比) (:MEAS:U? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电压测量值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> _不含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:U{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:U{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _不含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电流测量值(不含变比) (:MEAS:I? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电流测量值 (单位: A,保留两位小数)</returns>
public virtual async Task<double> _不含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:I{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:I{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _不含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的功率测量值(不含变比) (:MEAS:P? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>功率测量值 (单位: W,保留两位小数)</returns>
public virtual async Task<double> _不含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:P{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:P{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
// ---------------- 基础测量(含变比 CT ---------------- // ---------------- 基础测量(含变比 CT ----------------
public virtual async Task<string> _含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电压测量值(含变比) (:MEAS:U:CT? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电压测量值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> _含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:U{通道号}:CT?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:U{通道号}:CT?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电流测量值(含变比) (:MEAS:I:CT? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电流测量值 (单位: A,保留两位小数)</returns>
public virtual async Task<double> _含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:I{通道号}:CT?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:I{通道号}:CT?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的功率测量值(含变比) (:MEAS:P:CT? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>功率测量值 (单位: W,保留两位小数)</returns>
public virtual async Task<double> _含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:P{通道号}:CT?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:P{通道号}:CT?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
// ---------------- 其他高阶参数测量 ---------------- // ---------------- 其他高阶参数测量 ----------------
public virtual async Task<string> (int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的有功功率积分值 (:MEAS:WP? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>有功功率积分值 (单位: Wh,保留两位小数)</returns>
public virtual async Task<double> (int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:WP{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:WP{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> THD(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电压总谐波失真 (THD) (:MEAS:UTHD? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电压 THD 值 (单位: %,保留两位小数)</returns>
public virtual async Task<double> THD(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:UTHD{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:UTHD{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> THD(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电流总谐波失真 (THD) (:MEAS:ITHD? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电流 THD 值 (单位: %,保留两位小数)</returns>
public virtual async Task<double> THD(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:ITHD{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:ITHD{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> 线U12(CancellationToken ct = default) /// <summary>
/// 查询线电压 U12 (:MEAS:U12? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>线电压 U12 值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> 线U12(CancellationToken ct = default)
{ {
return await WriteReadAsync(":MEAS:U12?\r\n", "\n", ct); string resp = await WriteReadAsync(":MEAS:U12?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> P123(CancellationToken ct = default) /// <summary>
/// 查询三相总功率 P123 (:MEAS:P123? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>三相总功率值 (单位: W,保留两位小数)</returns>
public virtual async Task<double> P123(CancellationToken ct = default)
{ {
return await WriteReadAsync(":MEAS:P123?\r\n", "\n", ct); string resp = await WriteReadAsync(":MEAS:P123?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
// ---------------- 整流平均值 ---------------- // ---------------- 整流平均值 ----------------
public virtual async Task<string> (int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电压整流平均值(不含变比) (:MEAS:UMN? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电压整流平均值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> (int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:UMN{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:UMN{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> (int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电流整流平均值(不含变比) (:MEAS:IMN? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电流整流平均值 (单位: A,保留两位小数)</returns>
public virtual async Task<double> (int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:IMN{通道号}?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:IMN{通道号}?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电压整流平均值(含变比) (:MEAS:UMN:CT? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电压整流平均值 (单位: V,保留两位小数)</returns>
public virtual async Task<double> _含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:UMN{通道号}:CT?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:UMN{通道号}:CT?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
public virtual async Task<string> _含变比(int , CancellationToken ct = default) /// <summary>
/// 查询指定通道的电流整流平均值(含变比) (:MEAS:IMN:CT? 指令)
/// </summary>
/// <param name="通道号">通道编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>电流整流平均值 (单位: A,保留两位小数)</returns>
public virtual async Task<double> _含变比(int , CancellationToken ct = default)
{ {
return await WriteReadAsync($":MEAS:IMN{通道号}:CT?\r\n", "\n", ct); string resp = await WriteReadAsync($":MEAS:IMN{通道号}:CT?\r\n", "\n", ct);
return ExtractDouble(resp);
} }
#endregion #endregion
@@ -232,8 +458,10 @@ namespace DeviceCommand.Device
#region 5. #region 5.
/// <summary> /// <summary>
/// 发送自定义命令 /// 发送自定义命令(透传任意 SCPI 指令)
/// </summary> /// </summary>
/// <param name="命令">自定义命令字符串</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (string , CancellationToken ct = default) public virtual async Task (string , CancellationToken ct = default)
{ {
await SendAsync($"{命令}\r\n", ct); await SendAsync($"{命令}\r\n", ct);
@@ -241,4 +469,4 @@ namespace DeviceCommand.Device
#endregion #endregion
} }
} }
+142 -14
View File
@@ -8,30 +8,45 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary> /// <summary>
/// 环境箱(型号:RLT1000) 一拖三 /// 环境箱(型号:RLT1000) 一拖三,支持温度/湿度/水温/水流量的监测与定值控制 (Modbus TCP 协议)
/// </summary> /// </summary>
[ACPCommand] [ACPCommand]
public class RLT1000 : ModbusTcp public class RLT1000 : ModbusTcp
{ {
/// <summary>
/// 构造函数:传入 <see cref="TcpConfig"/> 初始化环境箱 Modbus TCP 通信参数
/// </summary>
public RLT1000(TcpConfig config) : base(config) public RLT1000(TcpConfig config) : base(config)
{ {
} }
// Modbus 从站号
private const byte SlaveId = 1; private const byte SlaveId = 1;
#region #region
/// <summary>
/// 读取环境箱运行状态监控字 (保持寄存器 5)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>状态监控字 (ushort 原始值)</returns>
public async Task<ushort> (CancellationToken ct = default) public async Task<ushort> (CancellationToken ct = default)
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 5, 1, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 5, 1, ct);
return re[0]; return re[0];
} }
public async Task<float> (CancellationToken ct = default) /// <summary>
/// 读取环境箱当前温度值 (保持寄存器 10-11)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>当前温度值 (单位: ℃,保留两位小数)</returns>
public async Task<double> (CancellationToken ct = default)
{ {
try try
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 10, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 10, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
catch (Exception) catch (Exception)
{ {
@@ -39,18 +54,28 @@ namespace DeviceCommand.Devices
} }
} }
public async Task<float> 湿(CancellationToken ct = default) /// <summary>
/// 读取环境箱当前湿度值 (保持寄存器 16-17)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>当前湿度值 (单位: %RH,保留两位小数)</returns>
public async Task<double> 湿(CancellationToken ct = default)
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 16, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 16, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
public async Task<float> (CancellationToken ct = default) /// <summary>
/// 读取环境箱当前水温值 (保持寄存器 22-23)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>当前水温值 (单位: ℃,保留两位小数)</returns>
public async Task<double> (CancellationToken ct = default)
{ {
try try
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 22, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 22, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
catch (Exception) catch (Exception)
{ {
@@ -58,119 +83,212 @@ namespace DeviceCommand.Devices
} }
} }
public async Task<float> 1(CancellationToken ct = default) /// <summary>
/// 读取当前 1 号水流量值 (保持寄存器 28-29)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>1 号水流量值 (保留两位小数)</returns>
public async Task<double> 1(CancellationToken ct = default)
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 28, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 28, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
public async Task<float> 2(CancellationToken ct = default) /// <summary>
/// 读取当前 2 号水流量值 (保持寄存器 34-35)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>2 号水流量值 (保留两位小数)</returns>
public async Task<double> 2(CancellationToken ct = default)
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 34, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 34, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
public async Task<float> 3(CancellationToken ct = default) /// <summary>
/// 读取当前 3 号水流量值 (保持寄存器 40-41)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>3 号水流量值 (保留两位小数)</returns>
public async Task<double> 3(CancellationToken ct = default)
{ {
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 40, 2, ct); ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 40, 2, ct);
return ConvertToFloat(re); return Math.Round(ConvertToFloat(re), 2);
} }
#endregion #endregion
#region #region
/// <summary>
/// 切换环境箱为本地控制模式 (寄存器 147 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 147, 0, ct); await WriteSingleRegisterAsync(SlaveId, 147, 0, ct);
} }
/// <summary>
/// 切换环境箱为远程控制模式 (寄存器 147 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 147, 1, ct); await WriteSingleRegisterAsync(SlaveId, 147, 1, ct);
} }
/// <summary>
/// 远程模式下关闭环境箱 (寄存器 148 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 148, 0, ct); await WriteSingleRegisterAsync(SlaveId, 148, 0, ct);
} }
/// <summary>
/// 远程模式下开启环境箱 (寄存器 148 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 148, 1, ct); await WriteSingleRegisterAsync(SlaveId, 148, 1, ct);
} }
/// <summary>
/// 远程模式复位结束 (寄存器 149 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 149, 0, ct); await WriteSingleRegisterAsync(SlaveId, 149, 0, ct);
} }
/// <summary>
/// 远程模式复位 (寄存器 149 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 149, 1, ct); await WriteSingleRegisterAsync(SlaveId, 149, 1, ct);
} }
/// <summary>
/// 切换为程序运行模式 (寄存器 153 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 153, 0, ct); await WriteSingleRegisterAsync(SlaveId, 153, 0, ct);
} }
/// <summary>
/// 切换为定值运行模式 (寄存器 153 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 153, 1, ct); await WriteSingleRegisterAsync(SlaveId, 153, 1, ct);
} }
/// <summary>
/// 定值模式下设置目标温度 (寄存器 154-155)
/// </summary>
/// <param name="温度">目标温度值 (单位: ℃)</param>
/// <param name="ct">取消令牌</param>
public async Task (float , CancellationToken ct = default) public async Task (float , CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(); var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 154, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 154, tmp, ct);
} }
/// <summary>
/// 定值模式下设置目标湿度 (寄存器 158-159)
/// </summary>
/// <param name="湿度">目标湿度值 (单位: %RH)</param>
/// <param name="ct">取消令牌</param>
public async Task 湿(float 湿, CancellationToken ct = default) public async Task 湿(float 湿, CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(湿); var tmp = ConvertFromFloat(湿);
await WriteMultipleRegistersAsync(SlaveId, 158, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 158, tmp, ct);
} }
/// <summary>
/// 定值模式下设置目标水温 (寄存器 162-163)
/// </summary>
/// <param name="水温">目标水温值 (单位: ℃)</param>
/// <param name="ct">取消令牌</param>
public async Task (float , CancellationToken ct = default) public async Task (float , CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(); var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 162, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 162, tmp, ct);
} }
/// <summary>
/// 定值模式下设置 1 号水流量目标值 (寄存器 164-165)
/// </summary>
/// <param name="水流量">1 号水流量目标值</param>
/// <param name="ct">取消令牌</param>
public async Task 1(float , CancellationToken ct = default) public async Task 1(float , CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(); var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 164, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 164, tmp, ct);
} }
/// <summary>
/// 定值模式下设置 2 号水流量目标值 (寄存器 166-167)
/// </summary>
/// <param name="水流量">2 号水流量目标值</param>
/// <param name="ct">取消令牌</param>
public async Task 2(float , CancellationToken ct = default) public async Task 2(float , CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(); var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 166, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 166, tmp, ct);
} }
/// <summary>
/// 定值模式下设置 3 号水流量目标值 (寄存器 168-169)
/// </summary>
/// <param name="水流量">3 号水流量目标值</param>
/// <param name="ct">取消令牌</param>
public async Task 3(float , CancellationToken ct = default) public async Task 3(float , CancellationToken ct = default)
{ {
var tmp = ConvertFromFloat(); var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 168, tmp, ct); await WriteMultipleRegistersAsync(SlaveId, 168, tmp, ct);
} }
/// <summary>
/// 设置水温模式不使用 (寄存器 1181 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task 使(CancellationToken ct = default) public async Task 使(CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 1181, 0, ct); await WriteSingleRegisterAsync(SlaveId, 1181, 0, ct);
} }
/// <summary>
/// 设置水温模式使用 (寄存器 1181 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task 使(CancellationToken ct = default) public async Task 使(CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 1181, 1, ct); await WriteSingleRegisterAsync(SlaveId, 1181, 1, ct);
} }
/// <summary>
/// 设置环境箱温度模式不使用 (寄存器 1182 写 0)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task 使(CancellationToken ct = default) public async Task 使(CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 1182, 0, ct); await WriteSingleRegisterAsync(SlaveId, 1182, 0, ct);
} }
/// <summary>
/// 设置环境箱温度模式使用 (寄存器 1182 写 1)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task 使(CancellationToken ct = default) public async Task 使(CancellationToken ct = default)
{ {
await WriteSingleRegisterAsync(SlaveId, 1182, 1, ct); await WriteSingleRegisterAsync(SlaveId, 1182, 1, ct);
@@ -180,6 +298,11 @@ namespace DeviceCommand.Devices
#region #region
/// <summary>
/// 将两个 16 位寄存器(大端序)转换为 IEEE754 单精度浮点数
/// </summary>
/// <param name="values">两个 ushort 寄存器值</param>
/// <returns>转换后的浮点数值</returns>
private float ConvertToFloat(ushort[] values) private float ConvertToFloat(ushort[] values)
{ {
if (values == null || values.Length < 2) return 0f; if (values == null || values.Length < 2) return 0f;
@@ -192,6 +315,11 @@ namespace DeviceCommand.Devices
return BitConverter.ToSingle(bytes, 0); return BitConverter.ToSingle(bytes, 0);
} }
/// <summary>
/// 将单精度浮点数转换为两个 16 位寄存器(大端序)
/// </summary>
/// <param name="value">待转换的浮点数值</param>
/// <returns>两个 ushort 寄存器值</returns>
private ushort[] ConvertFromFloat(float value) private ushort[] ConvertFromFloat(float value)
{ {
byte[] bytes = BitConverter.GetBytes(value); byte[] bytes = BitConverter.GetBytes(value);
@@ -203,4 +331,4 @@ namespace DeviceCommand.Devices
#endregion #endregion
} }
} }
+235 -37
View File
@@ -1,65 +1,119 @@
using Common.Attributes; using Common.Attributes;
using DeviceCommand.Base; using DeviceCommand.Base;
using Model.Models;
using System; using System;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
// 根据您的项目实际路径保留枚举的引用
// using 通讯驱动Base.枚举;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary>
/// S7200 负载工作模式枚举 (恒流/恒压/恒功率/恒阻等)
/// </summary>
public enum S7200负载模式_枚举 public enum S7200负载模式_枚举
{ {
/// <summary>恒流模式</summary>
CC, CC,
/// <summary>恒压模式</summary>
CV, CV,
/// <summary>恒功率模式</summary>
CP, CP,
/// <summary>恒阻模式</summary>
CR, CR,
/// <summary>恒压+恒流组合模式</summary>
CVCC, CVCC,
/// <summary>恒阻+恒流组合模式</summary>
CRCC, CRCC,
/// <summary>恒压+恒阻组合模式</summary>
CVCR, CVCR,
/// <summary>自动模式</summary>
AUTO AUTO
} }
/// <summary>
/// S7200 直流源工作模式枚举 (电压源/电流源/电池模拟/光伏模拟等)
/// </summary>
public enum S7200直流源模式_枚举 public enum S7200直流源模式_枚举
{ {
/// <summary>电压源模式</summary>
VOLTage, VOLTage,
/// <summary>电流源模式</summary>
CURRent, CURRent,
/// <summary>电池测试模式</summary>
BATTest, BATTest,
/// <summary>电池模拟模式</summary>
BATSim, BATSim,
/// <summary>光伏模拟模式</summary>
SOLar, SOLar,
/// <summary>EN50530 标准光伏曲线模式</summary>
EN50530, EN50530,
/// <summary>列表输出模式</summary>
LIST, LIST,
/// <summary>用户自定义波形模式</summary>
UDW, UDW,
/// <summary>方波输出模式</summary>
SQUare, SQUare,
/// <summary>三角波输出模式</summary>
TRIangle, TRIangle,
/// <summary>正弦波输出模式</summary>
SINusoid, SINusoid,
/// <summary>DIN40839 12V 启动曲线</summary>
DIN40839_12V, DIN40839_12V,
/// <summary>DIN40839 24V 启动曲线</summary>
DIN40839_24V, DIN40839_24V,
/// <summary>ISO16750 短时跌落 12V</summary>
IS016750_SHORTDROP_12V, IS016750_SHORTDROP_12V,
/// <summary>ISO16750 短时跌落 24V</summary>
IS016750_SHORTDROP_24V, IS016750_SHORTDROP_24V,
/// <summary>ISO16750 复位测试</summary>
IS016750_RESETTEST, IS016750_RESETTEST,
/// <summary>ISO16750 启动曲线 12V 第 1 组</summary>
IS016750_STARTINGPROFILE_12V_1, IS016750_STARTINGPROFILE_12V_1,
/// <summary>ISO16750 启动曲线 12V 第 2 组</summary>
IS016750_STARTINGPROFILE_12V_2, IS016750_STARTINGPROFILE_12V_2,
/// <summary>ISO16750 启动曲线 12V 第 3 组</summary>
IS016750_STARTINGPROFILE_12V_3, IS016750_STARTINGPROFILE_12V_3,
/// <summary>ISO16750 启动曲线 12V 第 4 组</summary>
IS016750_STARTINGPROFILE_12V_4, IS016750_STARTINGPROFILE_12V_4,
/// <summary>ISO16750 启动曲线 24V 第 1 组</summary>
IS016750_STARTINGPROFILE_24V_1, IS016750_STARTINGPROFILE_24V_1,
/// <summary>ISO16750 启动曲线 24V 第 2 组</summary>
IS016750_STARTINGPROFILE_24V_2, IS016750_STARTINGPROFILE_24V_2,
/// <summary>ISO16750 启动曲线 24V 第 3 组</summary>
IS016750_STARTINGPROFILE_24V_3, IS016750_STARTINGPROFILE_24V_3,
/// <summary>ISO16750 抛负载测试 A 12V</summary>
IS016750_LOADDUMP_TESTA_12V, IS016750_LOADDUMP_TESTA_12V,
/// <summary>ISO16750 抛负载测试 A 24V</summary>
IS016750_LOADDUMP_TESTA_24V, IS016750_LOADDUMP_TESTA_24V,
/// <summary>ISO16750 抛负载测试 B 12V</summary>
IS016750_LOADDUMP_TESTB_12V, IS016750_LOADDUMP_TESTB_12V,
/// <summary>ISO16750 抛负载测试 B 24V</summary>
IS016750_LOADDUMP_TESTB_24V, IS016750_LOADDUMP_TESTB_24V,
/// <summary>LV123 高压曲线 1</summary>
LV123_HV_1, LV123_HV_1,
/// <summary>LV123 高压曲线 2A</summary>
LV123_HV_2A, LV123_HV_2A,
/// <summary>LV123 高压曲线 2B</summary>
LV123_HV_2B, LV123_HV_2B,
/// <summary>LV123 高压曲线 3</summary>
LV123_HV_3 LV123_HV_3
} }
/// <summary>
/// S7200 工作模式枚举 (电源模式 / 负载模式)
/// </summary>
public enum S7200工作模式_枚举 public enum S7200工作模式_枚举
{ {
/// <summary>作为电源输出</summary>
, ,
/// <summary>作为负载吸收</summary>
, ,
} }
/// <summary> /// <summary>
/// 高压直流源载一体机 (适配 IT6000C SCPI指令集) /// 高压直流源载一体机 (适配 IT6000C SCPI 指令集),支持电源输出与负载吸收双向工作
/// </summary> /// </summary>
[ACPCommand] [ACPCommand]
public class S7200 : Tcp public class S7200 : Tcp
@@ -67,115 +121,239 @@ namespace DeviceCommand.Devices
// SCPI 指令结束符 // SCPI 指令结束符
private const string ScpiDelimiter = "\n"; private const string ScpiDelimiter = "\n";
#region /// <summary>
/// 构造函数:传入 <see cref="TcpConfig"/> 一次性初始化一体机通信参数
public async Task (CancellationToken ct = default) /// </summary>
public S7200(TcpConfig config) : base(config)
{ {
await SendAsync($"SYSTem:REMote{ScpiDelimiter}", ct); //[cite: 1]
} }
/// <summary>
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary>
/// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return 0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
}
#region
/// <summary>
/// 设置仪器为远程控制模式 (SYSTem:REMote)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:REMote{ScpiDelimiter}", ct);
}
/// <summary>
/// 设置电源工作模式 (电源/负载)。IT6000C 支持双向无缝切换,无需显式下发源载切换指令
/// </summary>
/// <param name="开关">工作模式枚举 (电源模式 / 负载模式)</param>
/// <param name="ct">取消令牌</param>
public async Task (S7200工作模式_枚举 , CancellationToken ct = default) public async Task (S7200工作模式_枚举 , CancellationToken ct = default)
{ {
// IT6000C 为双向无缝切换,底层硬件无需显式发送源载切换指令 // IT6000C 为双向无缝切换,底层硬件无需显式发送源载切换指令
// 保留此方法仅为兼容原有上位机的工步调用流程,直接返回即可 // 保留此方法仅为兼容原有上位机的工步调用流程
await Task.CompletedTask; await Task.CompletedTask;
} }
/// <summary>
/// 设置直流源工作模式 (FUNCtion 指令,如 VOLTage / CURRent / SOLar 等)
/// </summary>
/// <param name="模式">直流源工作模式枚举</param>
/// <param name="ct">取消令牌</param>
public async Task (S7200直流源模式_枚举 , CancellationToken ct = default) public async Task (S7200直流源模式_枚举 , CancellationToken ct = default)
{ {
// 原指令: MODE {模式} -> 现 IT6000C 指令: FUNCtion {模式} (如 VOLTage / CURRent)[cite: 1]
// 若枚举 ToString() 后不匹配,建议在此处加 Switch 转换
await SendAsync($"FUNCtion {模式}{ScpiDelimiter}", ct); await SendAsync($"FUNCtion {模式}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置负载工作模式。IT6000C 负载控制通过负向边界实现
/// </summary>
/// <param name="模式">负载工作模式枚举</param>
/// <param name="ct">取消令牌</param>
public async Task (S7200负载模式_枚举 , CancellationToken ct = default) public async Task (S7200负载模式_枚举 , CancellationToken ct = default)
{ {
// IT6000C 负载控制通过负向边界实现。若是想开启纯电阻模式,可调用 SINK:RESistance:STATe ON[cite: 1] // 想开启纯电阻模式,可调用 SINK:RESistance:STATe ON
await Task.CompletedTask; await Task.CompletedTask;
} }
/// <summary>
/// 开启或关闭通道输出 (OUTPut 指令)
/// </summary>
/// <param name="开关">true: 开启输出, false: 关闭输出</param>
/// <param name="ct">取消令牌</param>
public async Task (bool , CancellationToken ct = default) public async Task (bool , CancellationToken ct = default)
{ {
await SendAsync($"OUTPut {(开关 ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"OUTPut {(开关 ? 1 : 0)}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置恒流 (CC) 模式的电流值 (CURRent 指令)
/// </summary>
/// <param name="电流">电流设定值 (单位: A)</param>
/// <param name="ct">取消令牌</param>
public async Task CC模式电流(double , CancellationToken ct = default) public async Task CC模式电流(double , CancellationToken ct = default)
{ {
await SendAsync($"CURRent {电流}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"CURRent {电流}{ScpiDelimiter}", ct);
} }
public async Task CC电压H(double , CancellationToken ct = default) // 沿用原参数名“电流”,实际是设定电压上限 /// <summary>
/// 设置恒流 (CC) 模式的电压上限 (VOLTage:LIMit:POSitive 指令)
/// </summary>
/// <param name="电流">电压上限设定值 (单位: V),沿用原参数名</param>
/// <param name="ct">取消令牌</param>
public async Task CC电压H(double , CancellationToken ct = default)
{ {
await SendAsync($"VOLTage:LIMit:POSitive {电流}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"VOLTage:LIMit:POSitive {电流}{ScpiDelimiter}", ct);
} }
public async Task CC电压L(double , CancellationToken ct = default) // 沿用原参数名“电流”,实际是设定电压下限 /// <summary>
/// 设置恒流 (CC) 模式的电压下限 (VOLTage:LIMit:NEGative 指令)
/// </summary>
/// <param name="电流">电压下限设定值 (单位: V),沿用原参数名</param>
/// <param name="ct">取消令牌</param>
public async Task CC电压L(double , CancellationToken ct = default)
{ {
await SendAsync($"VOLTage:LIMit:NEGative {电流}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"VOLTage:LIMit:NEGative {电流}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置恒压 (CV) 模式的正向电流限制 (CURRent:LIMit:POSitive 指令)
/// </summary>
/// <param name="电流">正向电流限制值 (单位: A)</param>
/// <param name="ct">取消令牌</param>
public async Task CV正向电流(double , CancellationToken ct = default) public async Task CV正向电流(double , CancellationToken ct = default)
{ {
await SendAsync($"CURRent:LIMit:POSitive {电流}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"CURRent:LIMit:POSitive {电流}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置恒压 (CV) 模式的反向电流限制 (CURRent:LIMit:NEGative 指令)
/// </summary>
/// <param name="电流">反向电流限制值 (单位: A),负载模式需传负值</param>
/// <param name="ct">取消令牌</param>
public async Task CV反向电流(double , CancellationToken ct = default) public async Task CV反向电流(double , CancellationToken ct = default)
{ {
// 提醒: 此处传入的参数如果是正数建议在外部或此处转换为负值,因为载模式限制需要负数[cite: 1] // 载模式限制需要负数,若传入正数建议在外部转换为负值
await SendAsync($"CURRent:LIMit:NEGative {电流}{ScpiDelimiter}", ct); await SendAsync($"CURRent:LIMit:NEGative {电流}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置正向功率阈值 (POWer:LIMit:POSitive 指令)
/// </summary>
/// <param name="功率">正向功率阈值 (单位: W)</param>
/// <param name="ct">取消令牌</param>
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
await SendAsync($"POWer:LIMit:POSitive {功率}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"POWer:LIMit:POSitive {功率}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置反向功率阈值 (POWer:LIMit:NEGative 指令)
/// </summary>
/// <param name="功率">反向功率阈值 (单位: W),必须为负数</param>
/// <param name="ct">取消令牌</param>
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
// 提醒: 此处传入的参数必须为负数[cite: 1]
await SendAsync($"POWer:LIMit:NEGative {功率}{ScpiDelimiter}", ct); await SendAsync($"POWer:LIMit:NEGative {功率}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置输出电压值 (VOLTage 指令)
/// </summary>
/// <param name="电压">电压设定值 (单位: V)</param>
/// <param name="ct">取消令牌</param>
public async Task (double , CancellationToken ct = default) public async Task (double , CancellationToken ct = default)
{ {
await SendAsync($"VOLTage {电压}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"VOLTage {电压}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 清除保护状态 (OUTPut:PROTection:CLEar 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public async Task (CancellationToken ct = default) public async Task (CancellationToken ct = default)
{ {
await SendAsync($"OUTPut:PROTection:CLEar{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"OUTPut:PROTection:CLEar{ScpiDelimiter}", ct);
} }
/// <summary>
/// 开启或关闭过流保护 (OCP) 功能 (CURRent:PROTection:STATe 指令)
/// </summary>
/// <param name="v">true: 开启过流保护, false: 关闭过流保护</param>
/// <param name="ct">取消令牌</param>
public async Task OCP开关(bool v, CancellationToken ct = default) public async Task OCP开关(bool v, CancellationToken ct = default)
{ {
await SendAsync($"CURRent:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"CURRent:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 开启或关闭过压保护 (OVP) 功能 (VOLTage:PROTection:STATe 指令)
/// </summary>
/// <param name="v">true: 开启过压保护, false: 关闭过压保护</param>
/// <param name="ct">取消令牌</param>
public async Task OVP开关(bool v, CancellationToken ct = default) public async Task OVP开关(bool v, CancellationToken ct = default)
{ {
await SendAsync($"VOLTage:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"VOLTage:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置过压保护 (OVP) 的触发电压值 (VOLTage:PROTection 指令)
/// </summary>
/// <param name="oVP电压">过压保护触发电压值 (单位: V)</param>
/// <param name="ct">取消令牌</param>
public async Task OVP电压(double oVP电压, CancellationToken ct = default) public async Task OVP电压(double oVP电压, CancellationToken ct = default)
{ {
await SendAsync($"VOLTage:PROTection {oVP电压}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"VOLTage:PROTection {oVP电压}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置过流保护 (OCP) 的触发电流值 (CURRent:PROTection 指令)
/// </summary>
/// <param name="oCP电流">过流保护触发电流值 (单位: A)</param>
/// <param name="ct">取消令牌</param>
public async Task OCP电流(double oCP电流, CancellationToken ct = default) public async Task OCP电流(double oCP电流, CancellationToken ct = default)
{ {
await SendAsync($"CURRent:PROTection {oCP电流}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"CURRent:PROTection {oCP电流}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 开启或关闭过功率保护 (OPP) 功能 (POWer:PROTection:STATe 指令)
/// </summary>
/// <param name="v">true: 开启过功率保护, false: 关闭过功率保护</param>
/// <param name="ct">取消令牌</param>
public async Task (bool v, CancellationToken ct = default) public async Task (bool v, CancellationToken ct = default)
{ {
await SendAsync($"POWer:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"POWer:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置过功率保护 (OPP) 的触发功率值 (POWer:PROTection 指令)
/// </summary>
/// <param name="oPP功率">过功率保护触发功率值 (单位: W)</param>
/// <param name="ct">取消令牌</param>
public async Task (double oPP功率, CancellationToken ct = default) public async Task (double oPP功率, CancellationToken ct = default)
{ {
await SendAsync($"POWer:PROTection {oPP功率}{ScpiDelimiter}", ct); //[cite: 1] await SendAsync($"POWer:PROTection {oPP功率}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 发送自定义 SCPI 指令
/// </summary>
/// <param name="指令">自定义指令字符串</param>
/// <param name="ct">取消令牌</param>
public async Task (string , CancellationToken ct = default) public async Task (string , CancellationToken ct = default)
{ {
await SendAsync($"{指令}{ScpiDelimiter}", ct); await SendAsync($"{指令}{ScpiDelimiter}", ct);
@@ -185,29 +363,49 @@ namespace DeviceCommand.Devices
#region #region
/// <summary>
/// 查询设备识别信息 (*IDN? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>设备标识字符串 (制造商, 型号, 序列号, 固件版本)</returns>
public async Task<string> (CancellationToken ct = default) public async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1] return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary>
/// 查询实时输出电压 (MEASure:VOLTage? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>实时电压值 (单位: V,保留两位小数)</returns>
public async Task<double> (CancellationToken ct = default) public async Task<double> (CancellationToken ct = default)
{ {
var str = await WriteReadAsync($"MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1] var str = await WriteReadAsync($"MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(str, out double result) ? result : 0; return ExtractDouble(str);
} }
/// <summary>
/// 查询实时输出电流 (MEASure:CURRent? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>实时电流值 (单位: A,保留两位小数)</returns>
public async Task<double> (CancellationToken ct = default) public async Task<double> (CancellationToken ct = default)
{ {
var str = await WriteReadAsync($"MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1] var str = await WriteReadAsync($"MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(str, out double result) ? result : 0; return ExtractDouble(str);
} }
/// <summary>
/// 查询实时功率 (MEASure:POWer? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>实时功率值 (单位: W,保留两位小数)</returns>
public async Task<double> (CancellationToken ct = default) public async Task<double> (CancellationToken ct = default)
{ {
var str = await WriteReadAsync($"MEASure:POWer?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1] var str = await WriteReadAsync($"MEASure:POWer?{ScpiDelimiter}", ScpiDelimiter, ct);
return double.TryParse(str, out double result) ? result : 0; return ExtractDouble(str);
} }
#endregion #endregion
} }
} }
+143 -42
View File
@@ -5,6 +5,7 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -37,22 +38,48 @@ namespace DeviceCommand.Devices
/// </summary> /// </summary>
public enum TekMeasurementType public enum TekMeasurementType
{ {
/// <summary>幅度</summary>
AMPLitude, AMPLitude,
/// <summary>频率</summary>
FREQuency, FREQuency,
/// <summary>平均值</summary>
MEAN, MEAN,
/// <summary>峰峰值</summary>
PK2PK, PK2PK,
/// <summary>最大值</summary>
MAXimum, MAXimum,
/// <summary>最小值</summary>
MINimum MINimum
} }
#endregion #endregion
/// <summary>
/// Tektronix 示波器 (MSO 4/5/6 系列),支持采集控制、通道设置、自动测量与截图导出
/// </summary>
[ACPCommand] [ACPCommand]
public class TektronixMSO : Tcp public class TektronixMSO : Tcp
{ {
// SCPI 指令结束符 (消息终止符需使用 LF) // SCPI 指令结束符 (消息终止符需使用 LF)
private const string ScpiDelimiter = "\n"; private const string ScpiDelimiter = "\n";
/// <summary>
/// 从设备返回的 SCPI 响应字符串中提取数值部分并转换为 double。
/// 支持科学计数法(如 2.48E-03),结果保留两位小数。
/// </summary>
/// <param name="raw">设备返回的原始字符串</param>
/// <returns>提取到的数值(保留两位小数),解析失败时返回 0</returns>
private static double ExtractDouble(string raw)
{
if (string.IsNullOrWhiteSpace(raw)) return 0;
// 从响应中提取第一个数值(含正负号、小数点、科学计数法),可自动忽略单位后缀与命令头
var match = Regex.Match(raw, @"[+-]?(?:\d+\.?\d*|\.\d+)(?:[Ee][+-]?\d+)?");
return match.Success && double.TryParse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double val)
? Math.Round(val, 2)
: 0;
}
#region MSO44B ACP #region MSO44B ACP
public enum AcquireMode { Sample = 0, PeakDetect = 1, HiRes = 2, Average = 3, Envelope = 4 } public enum AcquireMode { Sample = 0, PeakDetect = 1, HiRes = 2, Average = 3, Envelope = 4 }
@@ -81,21 +108,32 @@ namespace DeviceCommand.Devices
#region 1. #region 1.
/// <summary>
/// 查询仪器识别码 (*IDN? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>设备标识字符串 (制造商, 型号, 序列号, 固件版本)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 返回仪器的标识代码[cite: 2]
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary>
/// 查询仪器是否处于忙碌状态,常用于同步操作 (BUSY? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>忙碌状态字符串 (0: 空闲, 1: 忙碌)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 查询仪器是否处于忙碌状态,常用于同步操作[cite: 2]
return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary>
/// 指示仪器执行信号路径校准 (SPC) (*CAL? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
// 指示仪器执行信号路径校准 (SPC)[cite: 2]
await SendAsync($"*CAL?{ScpiDelimiter}", ct); await SendAsync($"*CAL?{ScpiDelimiter}", ct);
} }
@@ -104,24 +142,33 @@ namespace DeviceCommand.Devices
#region 2. (Acquisition) #region 2. (Acquisition)
/// <summary> /// <summary>
/// 启动或停止采集 /// 启动或停止采集 (ACQuire:STATE 指令)
/// </summary> /// </summary>
/// <param name="运行">true: 启动采集 (RUN), false: 停止采集 (STOP)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (bool , CancellationToken ct = default) public virtual async Task (bool , CancellationToken ct = default)
{ {
// 启动、停止或返回采集状态[cite: 2]
string = ? "RUN" : "STOP"; string = ? "RUN" : "STOP";
await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct); await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询当前采集状态 (ACQuire:STATE? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>采集状态字符串 (RUN / STOP)</returns>
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 启动、停止或返回采集状态[cite: 2]
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary>
/// 设置采集模式 (ACQuire:MODe 指令)
/// </summary>
/// <param name="模式">采集模式枚举 (采样/峰值检测/高分辨率/平均/包络)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (TekAcquisitionMode , CancellationToken ct = default) public virtual async Task (TekAcquisitionMode , CancellationToken ct = default)
{ {
// 设置或查询采集模式[cite: 2]
await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct); await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct);
} }
@@ -129,23 +176,36 @@ namespace DeviceCommand.Devices
#region 3. (Horizontal) #region 3. (Horizontal)
/// <summary>
/// 设置水平刻度 (Scale),即每格代表的时间 (HORizontal:SCAle 指令)
/// </summary>
/// <param name="秒每格">水平刻度值 (单位: s/Div)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (double , CancellationToken ct = default) public virtual async Task (double , CancellationToken ct = default)
{ {
// 设置或查询水平刻度 (Scale)[cite: 2]
string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", , ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
/// <summary>
/// 设置水平记录长度 (HORizontal:RECOrdlength 指令)
/// </summary>
/// <param name="长度">记录长度(采样点数)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (long , CancellationToken ct = default) public virtual async Task (long , CancellationToken ct = default)
{ {
// 设置或查询水平记录长度[cite: 2]
await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct); await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct);
} }
public virtual async Task<string> (CancellationToken ct = default) /// <summary>
/// 查询水平采样率 (HORizontal:SAMPLERate? 指令)
/// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>水平采样率 (单位: Sa/s,保留两位小数)</returns>
public virtual async Task<double> (CancellationToken ct = default)
{ {
// 查询水平采样率[cite: 2] string resp = await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct);
return await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct); return ExtractDouble(resp);
} }
#endregion #endregion
@@ -153,18 +213,23 @@ namespace DeviceCommand.Devices
#region 4. (Vertical) #region 4. (Vertical)
/// <summary> /// <summary>
/// 全局启用或关闭某通道的显示 /// 全局启用或关闭某通道的显示 (DISplay:GLObal:CH:STATE 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道编号 (1 - 4)</param>
/// <param name="开启">true: 开启通道显示, false: 关闭通道显示</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , bool , CancellationToken ct = default) public virtual async Task (int , bool , CancellationToken ct = default)
{ {
// 4/5/6 Series MSO 标准通道显示指令[cite: 2]
string = ? "ON" : "OFF"; string = ? "ON" : "OFF";
await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct); await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct);
} }
/// <summary> /// <summary>
/// 设置指定通道的垂直缩放比例 (Volts/Div) /// 设置指定通道的垂直缩放比例 (CH:SCAle 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道编号 (1 - 4)</param>
/// <param name="伏特每格">垂直刻度值 (单位: V/Div)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , double , CancellationToken ct = default) public virtual async Task (int , double , CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:SCAle {1:E}{2}", , , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:SCAle {1:E}{2}", , , ScpiDelimiter);
@@ -172,8 +237,11 @@ namespace DeviceCommand.Devices
} }
/// <summary> /// <summary>
/// 设置指定通道的垂直位置 (Divisions) /// 设置指定通道的垂直位置 (CH:POSition 指令)
/// </summary> /// </summary>
/// <param name="通道号">通道编号 (1 - 4)</param>
/// <param name="格数">垂直位置偏移 (单位: Div)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , double , CancellationToken ct = default) public virtual async Task (int , double , CancellationToken ct = default)
{ {
string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:POSition {1:F2}{2}", , , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:POSition {1:F2}{2}", , , ScpiDelimiter);
@@ -185,43 +253,73 @@ namespace DeviceCommand.Devices
#region 5. (Measurement) #region 5. (Measurement)
/// <summary> /// <summary>
/// 动态添加并开启一个测量项 /// 动态添加并开启一个测量项 (MEASUrement:ADDNew 指令)。
/// 4/5/6 Series MSO 必须先动态创建测量项
/// </summary> /// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , CancellationToken ct = default) public virtual async Task (int , CancellationToken ct = default)
{ {
// 4/5/6 Series MSO 必须先动态创建测量项[cite: 2]
await SendAsync($"MEASUrement:ADDNew \"MEAS{}\"{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:ADDNew \"MEAS{}\"{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置测量项的测量类型 (MEASUrement:TYPe 指令)
/// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="类型">测量类型枚举 (幅度/频率/平均值/峰峰值等)</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , TekMeasurementType , CancellationToken ct = default) public virtual async Task (int , TekMeasurementType , CancellationToken ct = default)
{ {
// 设置或查询测量类型[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 设置测量项的测量源 (MEASUrement:SOUrce1 指令)
/// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="源名称">测量源名称 (如 "CH1", "CH2")</param>
/// <param name="ct">取消令牌</param>
public virtual async Task (int , string , CancellationToken ct = default) public virtual async Task (int , string , CancellationToken ct = default)
{ {
// 4/5/6 Series 中设置测量源[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:SOUrce1 {源名称}{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:MEAS{测量项编号}:SOUrce1 {源名称}{ScpiDelimiter}", ct);
} }
/// <summary>
/// 查询测量项的当前采集平均值 (MEASUrement:RESUlts:CURRentacq:MEAN? 指令)
/// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>测量项当前采集的平均值 (保留两位小数)</returns>
[Monitorable("测量项的当前采集平均值")] [Monitorable("测量项的当前采集平均值")]
public virtual async Task<string> (int , CancellationToken ct = default) public virtual async Task<double> (int , CancellationToken ct = default)
{ {
// 4/5/6 Series 获取当前采集值的标准方式,查询平均值[cite: 2] string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct); return ExtractDouble(resp);
} }
public virtual async Task<string> (int , CancellationToken ct = default) /// <summary>
/// 查询测量项的全局历史累积平均值 (MEASUrement:RESUlts:ALLAcqs:MEAN? 指令)
/// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>测量项全局平均值 (保留两位小数)</returns>
public virtual async Task<double> (int , CancellationToken ct = default)
{ {
// 获取所有采集(AllAcqs)历史累积的平均值[cite: 2] string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct); return ExtractDouble(resp);
} }
public virtual async Task<string> (int , CancellationToken ct = default) /// <summary>
/// 查询测量项的全局历史累积最大值 (MEASUrement:RESUlts:ALLAcqs:MAXimum? 指令)
/// </summary>
/// <param name="测量项编号">测量项编号</param>
/// <param name="ct">取消令牌</param>
/// <returns>测量项全局最大值 (保留两位小数)</returns>
public virtual async Task<double> (int , CancellationToken ct = default)
{ {
// 获取所有采集(AllAcqs)历史累积的最大值[cite: 2] string resp = await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct); return ExtractDouble(resp);
} }
#endregion #endregion
@@ -237,13 +335,13 @@ namespace DeviceCommand.Devices
{ {
string = "E:/temp1.png"; string = "E:/temp1.png";
// 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)[cite: 2] // 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)
await SendAsync($"SAVe:IMAGe \"{}\"{ScpiDelimiter}", ct); await SendAsync($"SAVe:IMAGe \"{}\"{ScpiDelimiter}", ct);
// 2. 发送 *OPC? 并等待返回 1,确保图像文件已经完全写入磁盘[cite: 2] // 2. 发送 *OPC? 并等待返回 1,确保图像文件已经完全写入磁盘
await WriteReadAsync($"*OPC?{ScpiDelimiter}", ScpiDelimiter, ct); await WriteReadAsync($"*OPC?{ScpiDelimiter}", ScpiDelimiter, ct);
// 3. 将文件内容读取到当前通信接口[cite: 2] // 3. 将文件内容读取到当前通信接口
await SendAsync($"FILESystem:READFile \"{}\"{ScpiDelimiter}", ct); await SendAsync($"FILESystem:READFile \"{}\"{ScpiDelimiter}", ct);
// 4. 提取底层的二进制块 (Block Data) // 4. 提取底层的二进制块 (Block Data)
@@ -252,7 +350,7 @@ namespace DeviceCommand.Devices
// 5. 将提取的二进制字节流写入上位机本地磁盘 // 5. 将提取的二进制字节流写入上位机本地磁盘
await File.WriteAllBytesAsync(, imageBytes, ct); await File.WriteAllBytesAsync(, imageBytes, ct);
// 6. 删除示波器上的临时文件,保持设备存储干净[cite: 2] // 6. 删除示波器上的临时文件,保持设备存储干净
await SendAsync($"FILESystem:DELEte \"{}\"{ScpiDelimiter}", ct); await SendAsync($"FILESystem:DELEte \"{}\"{ScpiDelimiter}", ct);
} }
@@ -331,41 +429,44 @@ namespace DeviceCommand.Devices
#region 7. (Status and Error) #region 7. (Status and Error)
/// <summary> /// <summary>
/// 清除所有的状态寄存器和错误/事件队列 /// 清除所有的状态寄存器和错误/事件队列 (*CLS 指令)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
public virtual async Task Async(CancellationToken ct = default) public virtual async Task Async(CancellationToken ct = default)
{ {
// 发送 *CLS 指令清空状态[cite: 2]
await SendAsync($"*CLS{ScpiDelimiter}", ct); await SendAsync($"*CLS{ScpiDelimiter}", ct);
} }
/// <summary> /// <summary>
/// 查询错误队列中当前的事件/错误数量 /// 查询错误队列中当前的事件/错误数量 (EVQty? 指令)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>事件队列中的事件数量字符串</returns>
public virtual async Task<string> Async(CancellationToken ct = default) public virtual async Task<string> Async(CancellationToken ct = default)
{ {
// 返回事件队列中的事件数量
return await WriteReadAsync($"EVQty?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"EVQty?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary> /// <summary>
/// 从错误队列中读取最早的一个事件/错误(包含代码和详细消息),并将其从队列中弹出 /// 从错误队列中读取最早的一个事件/错误(包含代码和详细消息),并将其从队列中弹出 (EVMsg? 指令)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>事件代码和消息字符串</returns>
public virtual async Task<string> Async(CancellationToken ct = default) public virtual async Task<string> Async(CancellationToken ct = default)
{ {
// 返回事件队列中的事件代码和消息
return await WriteReadAsync($"EVMsg?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"EVMsg?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
/// <summary> /// <summary>
/// 读取并清空队列中的所有错误和事件消息 /// 读取并清空队列中的所有错误和事件消息 (ALLEv? 指令)
/// </summary> /// </summary>
/// <param name="ct">取消令牌</param>
/// <returns>所有事件及其消息字符串</returns>
public virtual async Task<string> Async(CancellationToken ct = default) public virtual async Task<string> Async(CancellationToken ct = default)
{ {
// 返回所有事件及其消息
return await WriteReadAsync($"ALLEv?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"ALLEv?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
#endregion #endregion
} }
} }
+88 -13
View File
@@ -2,17 +2,31 @@
using NModbus; using NModbus;
using NModbus.Serial; using NModbus.Serial;
using System; using System;
using System.Collections.Concurrent;
using System.IO.Ports; using System.IO.Ports;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DeviceCommand.Flexible namespace DeviceCommand.Flexible
{ {
/// <summary>
/// 灵活型 Modbus RTU 串口通信类(免实例化,每次调用临时创建串口连接)。
/// 支持保持寄存器与线圈的读写操作,内部使用按串口名称粒度的通信锁保证同一串口同一时刻只有一个事务在执行,
/// 不同串口之间互不阻塞,适用于偶发性、无需保持长连接的 Modbus RTU 设备读写场景。
/// </summary>
[ACPCommand] [ACPCommand]
public static class FModbusRTU public static class FModbusRTU
{ {
private static readonly SemaphoreSlim _commLock = new(1, 1); // 按串口名称粒度的通信锁:同一串口同一时刻只有一个事务在执行,不同串口互不阻塞
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _commLocks = new();
/// <summary>获取指定串口的通信锁(不存在则自动创建)</summary>
private static SemaphoreSlim GetLock(string portName)
=> _commLocks.GetOrAdd(portName, _ => new SemaphoreSlim(1, 1));
/// <summary>
/// 创建串口实例并配置超时参数。
/// </summary>
private static SerialPort CreatePort( private static SerialPort CreatePort(
string portName, string portName,
int baudRate, int baudRate,
@@ -29,6 +43,9 @@ namespace DeviceCommand.Flexible
}; };
} }
/// <summary>
/// 基于指定串口创建 Modbus RTU 主站。
/// </summary>
private static IModbusMaster CreateMaster(SerialPort port) private static IModbusMaster CreateMaster(SerialPort port)
{ {
return new ModbusFactory().CreateRtuMaster(port); return new ModbusFactory().CreateRtuMaster(port);
@@ -36,7 +53,22 @@ namespace DeviceCommand.Flexible
#region Holding Register #region Holding Register
public static async Task<ushort[]> ReadHoldingRegistersAsync( /// <summary>
/// 读取保持寄存器(功能码 03),返回指定数量的寄存器值数组。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="slaveAddress">Modbus 从站站号 (1-247)</param>
/// <param name="startAddress">起始寄存器地址</param>
/// <param name="numberOfPoints">读取的寄存器数量</param>
/// <param name="dataBits">数据位,默认 8</param>
/// <param name="stopBits">停止位,默认 1 位</param>
/// <param name="parity">校验位,默认无校验</param>
/// <param name="readTimeout">读取超时时间(毫秒),默认 3000</param>
/// <param name="writeTimeout">写入超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>读取到的寄存器值数组</returns>
public static async Task<ushort[]> (
string portName, string portName,
int baudRate, int baudRate,
byte slaveAddress, byte slaveAddress,
@@ -49,7 +81,7 @@ namespace DeviceCommand.Flexible
int writeTimeout = 3000, int writeTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -69,11 +101,25 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
public static async Task WriteSingleRegisterAsync( /// <summary>
/// 写入单个保持寄存器(功能码 06)。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="slaveAddress">Modbus 从站站号 (1-247)</param>
/// <param name="registerAddress">要写入的寄存器地址</param>
/// <param name="value">要写入的寄存器值 (0-65535)</param>
/// <param name="dataBits">数据位,默认 8</param>
/// <param name="stopBits">停止位,默认 1 位</param>
/// <param name="parity">校验位,默认无校验</param>
/// <param name="readTimeout">读取超时时间(毫秒),默认 3000</param>
/// <param name="writeTimeout">写入超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
public static async Task (
string portName, string portName,
int baudRate, int baudRate,
byte slaveAddress, byte slaveAddress,
@@ -86,7 +132,7 @@ namespace DeviceCommand.Flexible
int writeTimeout = 3000, int writeTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -106,7 +152,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
@@ -114,7 +160,22 @@ namespace DeviceCommand.Flexible
#region Coil #region Coil
public static async Task<bool[]> ReadCoilsAsync( /// <summary>
/// 读取线圈状态(功能码 01),返回指定数量的线圈开关状态数组。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="slaveAddress">Modbus 从站站号 (1-247)</param>
/// <param name="startAddress">起始线圈地址</param>
/// <param name="numberOfPoints">读取的线圈数量</param>
/// <param name="dataBits">数据位,默认 8</param>
/// <param name="stopBits">停止位,默认 1 位</param>
/// <param name="parity">校验位,默认无校验</param>
/// <param name="readTimeout">读取超时时间(毫秒),默认 3000</param>
/// <param name="writeTimeout">写入超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>线圈状态数组,true 表示吸合/导通</returns>
public static async Task<bool[]> 线(
string portName, string portName,
int baudRate, int baudRate,
byte slaveAddress, byte slaveAddress,
@@ -127,7 +188,7 @@ namespace DeviceCommand.Flexible
int writeTimeout = 3000, int writeTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -147,11 +208,25 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
public static async Task WriteSingleCoilAsync( /// <summary>
/// 写入单个线圈(功能码 05),控制单个开关量输出。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="slaveAddress">Modbus 从站站号 (1-247)</param>
/// <param name="coilAddress">要写入的线圈地址</param>
/// <param name="value">线圈状态(true: 吸合/导通, false: 断开)</param>
/// <param name="dataBits">数据位,默认 8</param>
/// <param name="stopBits">停止位,默认 1 位</param>
/// <param name="parity">校验位,默认无校验</param>
/// <param name="readTimeout">读取超时时间(毫秒),默认 3000</param>
/// <param name="writeTimeout">写入超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
public static async Task 线(
string portName, string portName,
int baudRate, int baudRate,
byte slaveAddress, byte slaveAddress,
@@ -164,7 +239,7 @@ namespace DeviceCommand.Flexible
int writeTimeout = 3000, int writeTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -184,7 +259,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
+73 -13
View File
@@ -1,6 +1,7 @@
using Common.Attributes; using Common.Attributes;
using NModbus; using NModbus;
using System; using System;
using System.Collections.Concurrent;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -8,11 +9,24 @@ using System.Threading.Tasks;
namespace DeviceCommand.Flexible namespace DeviceCommand.Flexible
{ {
/// <summary>
/// 灵活型 Modbus TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
/// 支持保持寄存器与线圈的读写操作,内部使用按端点粒度的通信锁保证同一端点同一时刻只有一个事务在执行,
/// 不同端点之间互不阻塞,适用于偶发性、无需保持长连接的 Modbus TCP 设备读写场景。
/// </summary>
[ACPCommand] [ACPCommand]
public static class FModbusTCP public static class FModbusTCP
{ {
private static readonly SemaphoreSlim _commLock = new(1, 1); // 按端点粒度的通信锁:同一端点同一时刻只有一个事务在执行,不同端点互不阻塞
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _commLocks = new();
/// <summary>获取指定端点的通信锁(不存在则自动创建)</summary>
private static SemaphoreSlim GetLock(string ipAddress, int port)
=> _commLocks.GetOrAdd($"{ipAddress}:{port}", _ => new SemaphoreSlim(1, 1));
/// <summary>
/// 建立 TCP 连接并创建 Modbus TCP 主站。
/// </summary>
private static async Task<IModbusMaster> ConnectAsync(string ipAddress, int port, int sendTimeout, int receiveTimeout, CancellationToken ct) private static async Task<IModbusMaster> ConnectAsync(string ipAddress, int port, int sendTimeout, int receiveTimeout, CancellationToken ct)
{ {
var tcpClient = new TcpClient(); var tcpClient = new TcpClient();
@@ -26,7 +40,19 @@ namespace DeviceCommand.Flexible
#region Holding Registers #region Holding Registers
public static async Task<ushort[]> ReadHoldingRegistersAsync( /// <summary>
/// 读取保持寄存器(功能码 03),返回指定数量的寄存器值数组。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">Modbus TCP 端口号,默认常用 502</param>
/// <param name="slaveAddress">Modbus 从站站号(单元标识符)</param>
/// <param name="startAddress">起始寄存器地址</param>
/// <param name="numberOfPoints">读取的寄存器数量</param>
/// <param name="sendTimeout">发送/连接超时时间(毫秒),默认 3000</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>读取到的寄存器值数组</returns>
public static async Task<ushort[]> (
string ipAddress, string ipAddress,
int port, int port,
byte slaveAddress, byte slaveAddress,
@@ -36,7 +62,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000, int receiveTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -47,11 +73,22 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
public static async Task WriteSingleRegisterAsync( /// <summary>
/// 写入单个保持寄存器(功能码 06)。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">Modbus TCP 端口号,默认常用 502</param>
/// <param name="slaveAddress">Modbus 从站站号(单元标识符)</param>
/// <param name="registerAddress">要写入的寄存器地址</param>
/// <param name="value">要写入的寄存器值 (0-65535)</param>
/// <param name="sendTimeout">发送/连接超时时间(毫秒),默认 3000</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
public static async Task (
string ipAddress, string ipAddress,
int port, int port,
byte slaveAddress, byte slaveAddress,
@@ -61,7 +98,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000, int receiveTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -71,7 +108,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
@@ -79,7 +116,19 @@ namespace DeviceCommand.Flexible
#region Coils #region Coils
public static async Task<bool[]> ReadCoilsAsync( /// <summary>
/// 读取线圈状态(功能码 01),返回指定数量的线圈开关状态数组。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">Modbus TCP 端口号,默认常用 502</param>
/// <param name="slaveAddress">Modbus 从站站号(单元标识符)</param>
/// <param name="startAddress">起始线圈地址</param>
/// <param name="numberOfPoints">读取的线圈数量</param>
/// <param name="sendTimeout">发送/连接超时时间(毫秒),默认 3000</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>线圈状态数组,true 表示吸合/导通</returns>
public static async Task<bool[]> 线(
string ipAddress, string ipAddress,
int port, int port,
byte slaveAddress, byte slaveAddress,
@@ -89,7 +138,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000, int receiveTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -100,11 +149,22 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
public static async Task WriteSingleCoilAsync( /// <summary>
/// 写入单个线圈(功能码 05),控制单个开关量输出。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">Modbus TCP 端口号,默认常用 502</param>
/// <param name="slaveAddress">Modbus 从站站号(单元标识符)</param>
/// <param name="coilAddress">要写入的线圈地址</param>
/// <param name="value">线圈状态(true: 吸合/导通, false: 断开)</param>
/// <param name="sendTimeout">发送/连接超时时间(毫秒),默认 3000</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),默认 3000</param>
/// <param name="ct">异步取消令牌</param>
public static async Task 线(
string ipAddress, string ipAddress,
int port, int port,
byte slaveAddress, byte slaveAddress,
@@ -114,7 +174,7 @@ namespace DeviceCommand.Flexible
int receiveTimeout = 3000, int receiveTimeout = 3000,
CancellationToken ct = default) CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable;
@@ -124,7 +184,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
+48 -7
View File
@@ -1,5 +1,6 @@
using Common.Attributes; using Common.Attributes;
using System; using System;
using System.Collections.Concurrent;
using System.IO.Ports; using System.IO.Ports;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -7,11 +8,25 @@ using System.Threading.Tasks;
namespace DeviceCommand.Flexible namespace DeviceCommand.Flexible
{ {
/// <summary>
/// 灵活型串口通信类(免实例化,每次调用临时创建串口连接)。
/// 支持只发送指令、发送并读取应答两种最常用操作,
/// 内部使用按串口名称粒度的通信锁保证同一串口同一时刻只有一个事务在执行,
/// 不同串口之间互不阻塞,适用于偶发性、无需保持长连接的串口设备通信场景(如示波器、电源的 SCPI 指令)。
/// </summary>
[ACPCommand] [ACPCommand]
public static class FSerialPort public static class FSerialPort
{ {
private static readonly SemaphoreSlim _commLock = new(1, 1); // 按串口名称粒度的通信锁:同一串口同一时刻只有一个事务在执行,不同串口互不阻塞
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _commLocks = new();
/// <summary>获取指定串口的通信锁(不存在则自动创建)</summary>
private static SemaphoreSlim GetLock(string portName)
=> _commLocks.GetOrAdd(portName, _ => new SemaphoreSlim(1, 1));
/// <summary>
/// 创建串口实例并配置 UTF8 编码与超时参数。
/// </summary>
private static SerialPort CreatePort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int sendTimeout,int receiveTimeout) private static SerialPort CreatePort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int sendTimeout,int receiveTimeout)
{ {
return new SerialPort(portName, baudRate, parity, dataBits, stopBits) return new SerialPort(portName, baudRate, parity, dataBits, stopBits)
@@ -24,9 +39,21 @@ namespace DeviceCommand.Flexible
#region #region
public static async Task SendAsync(string portName,int baudRate,Parity parity,int dataBits,StopBits stopBits,int sendTimeout,int receiveTimeout,string command,CancellationToken ct = default) /// <summary>
/// 向串口发送一条字符串指令(只发送,不等待应答)。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="parity">校验位</param>
/// <param name="dataBits">数据位,常用 8</param>
/// <param name="stopBits">停止位</param>
/// <param name="sendTimeout">发送超时时间(毫秒)</param>
/// <param name="receiveTimeout">接收超时时间(毫秒)</param>
/// <param name="command">要发送的指令字符串</param>
/// <param name="ct">异步取消令牌</param>
public static async Task (string portName,int baudRate,Parity parity,int dataBits,StopBits stopBits,int sendTimeout,int receiveTimeout,string command,CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -44,7 +71,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
@@ -52,9 +79,23 @@ namespace DeviceCommand.Flexible
#region #region
public static async Task<string> SendReadAsync(string portName,int baudRate, Parity parity, int dataBits, StopBits stopBits,int sendTimeout, int receiveTimeout,string command,string delimiter = "\n", CancellationToken ct = default) /// <summary>
/// 向串口发送一条字符串指令并读取应答,读取到结束符为止(应答不含结束符)。
/// </summary>
/// <param name="portName">串口名称,如 "COM1"</param>
/// <param name="baudRate">波特率,如 9600、115200</param>
/// <param name="parity">校验位</param>
/// <param name="dataBits">数据位,常用 8</param>
/// <param name="stopBits">停止位</param>
/// <param name="sendTimeout">发送超时时间(毫秒)</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),超时未收到结束符抛出超时异常</param>
/// <param name="command">要发送的指令字符串</param>
/// <param name="delimiter">应答结束符,默认换行符 "\n"</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>去除结束符并去除首尾空白后的应答字符串</returns>
public static async Task<string> (string portName,int baudRate, Parity parity, int dataBits, StopBits stopBits,int sendTimeout, int receiveTimeout,string command,string delimiter = "\n", CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(portName).WaitAsync(ct);
try try
{ {
using var port = CreatePort( using var port = CreatePort(
@@ -95,7 +136,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(portName).Release();
} }
} }
+58 -12
View File
@@ -1,19 +1,39 @@
using Common.Attributes; using Common.Attributes;
using System.Collections.Concurrent;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
namespace DeviceCommand.Flexible namespace DeviceCommand.Flexible
{ {
/// <summary>
/// 灵活型 TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
/// 支持字节/文本发送、定长字节读取、按结束符读取文本行四种操作,
/// 内部使用按端点粒度的通信锁保证同一端点同一时刻只有一个 TCP 事务在执行,
/// 不同端点之间互不阻塞,适用于偶发性、无需保持长连接的 TCP 设备通信场景。
/// </summary>
[ACPCommand] [ACPCommand]
public static class FTCP public static class FTCP
{ {
private static readonly SemaphoreSlim _commLock = new(1, 1); // 按端点粒度的通信锁:同一端点同一时刻只有一个事务在执行,不同端点互不阻塞
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _commLocks = new();
/// <summary>获取指定端点的通信锁(不存在则自动创建)</summary>
private static SemaphoreSlim GetLock(string ipAddress, int port)
=> _commLocks.GetOrAdd($"{ipAddress}:{port}", _ => new SemaphoreSlim(1, 1));
#region Send #region Send
public static async Task SendAsync(string ipAddress,int port,int sendTimeout, byte[] buffer, CancellationToken ct = default) /// <summary>
/// 向指定 TCP 端点发送一段字节数据(发送完成后立即断开)。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">TCP 端口号</param>
/// <param name="sendTimeout">发送超时时间(毫秒)</param>
/// <param name="buffer">要发送的字节数组</param>
/// <param name="ct">异步取消令牌</param>
public static async Task (string ipAddress,int port,int sendTimeout, byte[] buffer, CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var client = new TcpClient(); using var client = new TcpClient();
@@ -26,22 +46,39 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
public static Task SendAsync(string ipAddress, int port,int sendTimeout, string text,CancellationToken ct = default) /// <summary>
/// 向指定 TCP 端点发送一段文本(以 UTF8 编码为字节后发送,发送完成后立即断开)。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">TCP 端口号</param>
/// <param name="sendTimeout">发送超时时间(毫秒)</param>
/// <param name="text">要发送的文本字符串</param>
/// <param name="ct">异步取消令牌</param>
public static Task (string ipAddress, int port,int sendTimeout, string text,CancellationToken ct = default)
{ {
return SendAsync( ipAddress, port, sendTimeout, Encoding.UTF8.GetBytes(text),ct); return ( ipAddress, port, sendTimeout, Encoding.UTF8.GetBytes(text),ct);
} }
#endregion #endregion
#region Read #region Read
public static async Task<byte[]> ReadAsync(string ipAddress,int port,int receiveTimeout,int length,CancellationToken ct = default) /// <summary>
/// 连接指定 TCP 端点并读取指定长度的字节数据(连接关闭或读满为止)。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">TCP 端口号</param>
/// <param name="receiveTimeout">接收超时时间(毫秒)</param>
/// <param name="length">要读取的字节长度</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>实际读取到的字节数组(对端提前关闭时可能短于请求长度)</returns>
public static async Task<byte[]> (string ipAddress,int port,int receiveTimeout,int length,CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var client = new TcpClient(); using var client = new TcpClient();
@@ -68,13 +105,22 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
public static async Task<string> ReadLineAsync( string ipAddress, int port, int receiveTimeout, string delimiter = "\n",CancellationToken ct = default) /// <summary>
/// 连接指定 TCP 端点并读取一行文本,读取到结束符为止(返回内容不含结束符)。
/// </summary>
/// <param name="ipAddress">设备 IP 地址</param>
/// <param name="port">TCP 端口号</param>
/// <param name="receiveTimeout">接收超时时间(毫秒),超时未收到结束符抛出超时异常</param>
/// <param name="delimiter">文本行结束符,默认换行符 "\n"</param>
/// <param name="ct">异步取消令牌</param>
/// <returns>去除结束符并去除首尾空白后的文本行</returns>
public static async Task<string> ( string ipAddress, int port, int receiveTimeout, string delimiter = "\n",CancellationToken ct = default)
{ {
await _commLock.WaitAsync(ct); await GetLock(ipAddress, port).WaitAsync(ct);
try try
{ {
using var client = new TcpClient(); using var client = new TcpClient();
@@ -106,7 +152,7 @@ namespace DeviceCommand.Flexible
} }
finally finally
{ {
_commLock.Release(); GetLock(ipAddress, port).Release();
} }
} }
+16 -2
View File
@@ -15,8 +15,22 @@ namespace DeviceEditModule
containerRegistry.RegisterDialog<DialogMangerView, DialogMangerViewModel>("DialogMangerView"); containerRegistry.RegisterDialog<DialogMangerView, DialogMangerViewModel>("DialogMangerView");
// 设备编辑 View 注册为 Navigation(被动态解析后作为 Tab 内容嵌入 DialogMangerView // 设备编辑 View 注册为 Navigation(被动态解析后作为 Tab 内容嵌入 DialogMangerView
//containerRegistry.RegisterForNavigation<IT7800EView>("IT7800EView"); containerRegistry.RegisterForNavigation<DG1000ZView>("DG1000ZView");
//containerRegistry.Register<SPAW7000ViewModel>(); containerRegistry.RegisterForNavigation<IT6720View>("IT6720View");
containerRegistry.RegisterForNavigation<PW8001View>("PW8001View");
containerRegistry.RegisterForNavigation<ANEVH80View>("ANEVH80View");
containerRegistry.RegisterForNavigation<Chroma61800View>("Chroma61800View");
containerRegistry.RegisterForNavigation<MCc30WView>("MCc30WView");
containerRegistry.RegisterForNavigation<RLT1000View>("RLT1000View");
containerRegistry.RegisterForNavigation<S7200View>("S7200View");
containerRegistry.Register<DG1000ZViewModel>();
containerRegistry.Register<IT6720ViewModel>();
containerRegistry.Register<PW8001ViewModel>();
containerRegistry.Register<ANEVH80ViewModel>();
containerRegistry.Register<Chroma61800ViewModel>();
containerRegistry.Register<MCc30WViewModel>();
containerRegistry.Register<RLT1000ViewModel>();
containerRegistry.Register<S7200ViewModel>();
} }
} }
} }
@@ -0,0 +1,149 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// ANEVH 高压源载一体机 控制面板 ViewModel
/// </summary>
public class ANEVH80ViewModel : NavigateViewModelBase, IDisposable
{
private readonly DeviceManager _dm;
private ANEVH80? _dev;
private CancellationTokenSource? _cts;
private string _deviceName = "ANEVH80";
public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
private bool _isConnected;
public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
private bool _isBusy;
public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
#region
private double _voltage;
public double Voltage { get => _voltage; set => SetProperty(ref _voltage, value); }
private double _current;
public double Current { get => _current; set => SetProperty(ref _current, value); }
private double _power;
public double Power { get => _power; set => SetProperty(ref _power, value); }
#endregion
#region (SINK)
private double _sinkCurrent;
public double SinkCurrent { get => _sinkCurrent; set => SetProperty(ref _sinkCurrent, value); }
private double _sinkPower;
public double SinkPower { get => _sinkPower; set => SetProperty(ref _sinkPower, value); }
#endregion
#region
private double _measuredVoltage;
public double MeasuredVoltage { get => _measuredVoltage; set => SetProperty(ref _measuredVoltage, value); }
private double _measuredCurrent;
public double MeasuredCurrent { get => _measuredCurrent; set => SetProperty(ref _measuredCurrent, value); }
private double _measuredPower;
public double MeasuredPower { get => _measuredPower; set => SetProperty(ref _measuredPower, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand Reset { get; }
public ICommand OutOn { get; } public ICommand OutOff { get; }
public ICommand SetV { get; } public ICommand SetI { get; } public ICommand SetP { get; }
public ICommand SetSinkI { get; } public ICommand SetSinkP { get; }
public ICommand QMeas { get; }
#endregion
public ANEVH80ViewModel(IContainerProvider cp) : base(cp)
{
_dm = cp.Resolve<DeviceManager>();
QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.Async(Ct()))));
Reset = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Ct()); Log("设备已复位"); }));
OutOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(true, Ct()); Log("输出已开启"); }));
OutOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(false, Ct()); Log("输出已关闭"); }));
SetV = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Voltage, Ct()); Log($"电压={Voltage}V"); }));
SetI = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Current, Ct()); Log($"电流={Current}A"); }));
SetP = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Power, Ct()); Log($"功率={Power}W"); }));
SetSinkI = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(SinkCurrent, Ct()); Log($"负载电流={SinkCurrent}A"); }));
SetSinkP = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(SinkPower, Ct()); Log($"负载功率={SinkPower}W"); }));
QMeas = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVoltage = await _dev!.Async(Ct());
MeasuredCurrent = await _dev!.Async(Ct());
MeasuredPower = await _dev!.Async(Ct());
Log($"测量→V:{MeasuredVoltage} I:{MeasuredCurrent} P:{MeasuredPower}");
}));
Initialize();
}
#region / Navigation
public void Initialize(string? deviceName = null)
{
ANEVH80? found = null; string? fn = null;
if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is ANEVH80 e)
{ found = e; fn = deviceName; }
else
{
foreach (var kv in _dm.DeviceMap)
if (kv.Value is ANEVH80 it) { found = it; fn = kv.Key; break; }
}
_dev = found;
DeviceName = fn ?? "ANEVH80 (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 ANEVH80 设备");
}
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
private void Log(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
}
@@ -0,0 +1,142 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// Chroma61800 交流源一拖三 控制面板 ViewModel
/// </summary>
public class Chroma61800ViewModel : NavigateViewModelBase, IDisposable
{
private readonly DeviceManager _dm;
private Chroma61800? _dev;
private CancellationTokenSource? _cts;
private string _deviceName = "Chroma61800";
public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
private bool _isConnected;
public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
private bool _isBusy;
public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
#region
private double _voltage; public double Voltage { get => _voltage; set => SetProperty(ref _voltage, value); }
private double _frequency = 50; public double Frequency { get => _frequency; set => SetProperty(ref _frequency, value); }
private string _waveform = "SINE"; public string Waveform { get => _waveform; set => SetProperty(ref _waveform, value); }
#endregion
#region
private double _measuredVoltage;
public double MeasuredVoltage { get => _measuredVoltage; set => SetProperty(ref _measuredVoltage, value); }
private double _measuredCurrent;
public double MeasuredCurrent { get => _measuredCurrent; set => SetProperty(ref _measuredCurrent, value); }
private double _measuredFrequency;
public double MeasuredFrequency { get => _measuredFrequency; set => SetProperty(ref _measuredFrequency, value); }
private double _measuredPower;
public double MeasuredPower { get => _measuredPower; set => SetProperty(ref _measuredPower, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand Reset { get; }
public ICommand OutOn { get; } public ICommand OutOff { get; }
public ICommand SetV { get; } public ICommand SetF { get; } public ICommand SetWave { get; }
public ICommand SetThreePhase { get; } public ICommand SetSinglePhase { get; }
public ICommand QMeas { get; }
#endregion
public Chroma61800ViewModel(IContainerProvider cp) : base(cp)
{
_dm = cp.Resolve<DeviceManager>();
QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.Async(Ct()))));
Reset = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Ct()); Log("设备已复位"); }));
OutOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(true, Ct()); Log("输出已开启"); }));
OutOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(false, Ct()); Log("输出已关闭"); }));
SetV = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Voltage, Ct()); Log($"电压={Voltage}V"); }));
SetF = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Frequency, Ct()); Log($"频率={Frequency}Hz"); }));
SetWave = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Waveform, Ct()); Log($"波形={Waveform}"); }));
SetThreePhase = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(true, Ct()); Log("三相模式"); }));
SetSinglePhase = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(false, Ct()); Log("单相模式"); }));
QMeas = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVoltage = await _dev!.Async(Ct());
MeasuredCurrent = await _dev!.Async(Ct());
MeasuredFrequency = await _dev!.Async(Ct());
MeasuredPower = await _dev!.Async(Ct());
Log($"测量→V:{MeasuredVoltage} I:{MeasuredCurrent} F:{MeasuredFrequency} P:{MeasuredPower}");
}));
Initialize();
}
#region / Navigation
public void Initialize(string? deviceName = null)
{
Chroma61800? found = null; string? fn = null;
if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is Chroma61800 e)
{ found = e; fn = deviceName; }
else
{
foreach (var kv in _dm.DeviceMap)
if (kv.Value is Chroma61800 it) { found = it; fn = kv.Key; break; }
}
_dev = found;
DeviceName = fn ?? "Chroma61800 (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 Chroma61800 设备");
}
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
private void Log(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
}
+86 -98
View File
@@ -1,133 +1,122 @@
using DeviceCommand.Devices; using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System; using System;
using System.Collections.Generic; using System.Threading;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable; using UIShare.GlobalVariable;
using UIShare.ViewModelBase; using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels namespace DeviceEditModule.ViewModels
{ {
/// <summary>
/// DG1000Z 信号发生器一拖三 控制面板 ViewModel
/// </summary>
public class DG1000ZViewModel : NavigateViewModelBase, IDisposable public class DG1000ZViewModel : NavigateViewModelBase, IDisposable
{ {
#region private readonly DeviceManager _dm;
private readonly DeviceManager _deviceManager; private DG1000Z? _dev;
private DG1000Z? _device;
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "DG1000Z"; private string _deviceName = "DG1000Z";
public string DeviceName public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
{
get => _deviceName;
set => SetProperty(ref _deviceName, value);
}
private bool _isConnected; private bool _isConnected;
public bool IsConnected public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
{
get => _isConnected;
set => SetProperty(ref _isConnected, value);
}
private bool _isBusy; private bool _isBusy;
/// <summary>正在执行设备命令时为 true,用于 UI 忙碌状态指示。</summary> public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
public bool IsBusy
#region
private int _channel = 1; public int Channel { get => _channel; set => SetProperty(ref _channel, value); }
private double _frequency = 1000; public double Frequency { get => _frequency; set => SetProperty(ref _frequency, value); }
private double _amplitude = 5; public double Amplitude { get => _amplitude; set => SetProperty(ref _amplitude, value); }
private double _offsetVoltage; public double OffsetVoltage { get => _offsetVoltage; set => SetProperty(ref _offsetVoltage, value); }
private double _dutyCycle = 50; public double DutyCycle { get => _dutyCycle; set => SetProperty(ref _dutyCycle, value); }
#endregion
#region
private double _measuredFrequency;
public double MeasuredFrequency { get => _measuredFrequency; set => SetProperty(ref _measuredFrequency, value); }
private double _measuredAmplitude;
public double MeasuredAmplitude { get => _measuredAmplitude; set => SetProperty(ref _measuredAmplitude, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand Reset { get; }
public ICommand OutOn { get; } public ICommand OutOff { get; }
public ICommand SetFreq { get; } public ICommand SetAmp { get; } public ICommand SetOffset { get; }
public ICommand SetDuty { get; } public ICommand QueryFreq { get; } public ICommand QueryAmp { get; }
public ICommand QueryCounter { get; } public ICommand QueryError { get; }
#endregion
public DG1000ZViewModel(IContainerProvider cp) : base(cp)
{ {
get => _isBusy; _dm = cp.Resolve<DeviceManager>();
set => SetProperty(ref _isBusy, value); QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.(Ct()))));
} Reset = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("设备已重置"); }));
private string _responseLog = string.Empty; OutOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, true, Ct()); Log($"CH{Channel}输出开"); }));
/// <summary>命令响应日志(最新消息在顶部)。</summary> OutOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, false, Ct()); Log($"CH{Channel}输出关"); }));
public string ResponseLog SetFreq = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, Frequency, Ct()); Log($"CH{Channel}频率={Frequency}Hz"); }));
{ SetAmp = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, Amplitude, Ct()); Log($"CH{Channel}幅度={Amplitude}Vpp"); }));
get => _responseLog; SetOffset = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, OffsetVoltage, Ct()); Log($"CH{Channel}偏移={OffsetVoltage}Vdc"); }));
set => SetProperty(ref _responseLog, value); SetDuty = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Channel, DutyCycle, Ct()); Log($"CH{Channel}占空比={DutyCycle}%"); }));
QueryFreq = new DelegateCommand(async () => await Exec(async () => { MeasuredFrequency = await _dev!.(Channel, Ct()); Log($"CH{Channel}频率={MeasuredFrequency}Hz"); }));
QueryAmp = new DelegateCommand(async () => await Exec(async () => { MeasuredAmplitude = await _dev!.(Channel, Ct()); Log($"CH{Channel}幅度={MeasuredAmplitude}Vpp"); }));
QueryCounter = new DelegateCommand(async () => await Exec(async () => { MeasuredFrequency = await _dev!.(Ct()); Log($"频率计={MeasuredFrequency}Hz"); }));
QueryError = new DelegateCommand(async () => await Exec(async () => Log("错误:" + await _dev!.(Ct()))));
Initialize();
} }
#endregion
#region
#endregion
public DG1000ZViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#region / Navigation #region / Navigation
/// <summary>
/// 从 DeviceManager 中查找DG1000Z设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null) public void Initialize(string? deviceName = null)
{ {
DG1000Z? found = null; DG1000Z? found = null; string? fn = null;
string? foundName = null; if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is DG1000Z e)
if (deviceName != null && { found = e; fn = deviceName; }
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is DG1000Z e)
{
found = e;
foundName = deviceName;
}
else else
{ {
foreach (var kv in _deviceManager.DeviceMap) foreach (var kv in _dm.DeviceMap)
{ if (kv.Value is DG1000Z it) { found = it; fn = kv.Key; break; }
if (kv.Value is DG1000Z it)
{
found = it;
foundName = kv.Key;
break;
}
}
} }
_dev = found;
_device = found; DeviceName = fn ?? "DG1000Z (未找到)";
DeviceName = foundName ?? "IT7800E (未找到)"; IsConnected = _dev?.IsConnected ?? false;
IsConnected = _device?.IsConnected ?? false; Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
AppendLog(found != null : "未在 DeviceManager 中找到 DG1000Z 设备");
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
} }
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region #region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token; private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action) private async Task Exec(Func<Task> action)
{ {
if (_device == null) if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return; if (IsBusy) return;
IsBusy = true; IsBusy = true;
try try
{ {
await action(); await action();
IsConnected = _device.IsConnected; IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
} }
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
} }
private void AppendLog(string message) private void Log(string message)
{ {
var line = $"[{DateTime.Now:HH:mm:ss}] {message}"; var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000 ResponseLog = ResponseLog.Length > 4000
@@ -136,12 +125,11 @@ namespace DeviceEditModule.ViewModels
} }
#endregion #endregion
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
} }
} }
+88 -98
View File
@@ -1,133 +1,124 @@
using DeviceCommand.Devices; using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System; using System;
using System.Collections.Generic; using System.Threading;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable; using UIShare.GlobalVariable;
using UIShare.ViewModelBase; using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels namespace DeviceEditModule.ViewModels
{ {
/// <summary>
/// IT6720 低压电源一拖三 控制面板 ViewModel
/// </summary>
public class IT6720ViewModel : NavigateViewModelBase, IDisposable public class IT6720ViewModel : NavigateViewModelBase, IDisposable
{ {
#region private readonly DeviceManager _dm;
private readonly DeviceManager _deviceManager; private IT6720? _dev;
private IT6720? _device;
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "IT6720"; private string _deviceName = "IT6720";
public string DeviceName public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
{
get => _deviceName;
set => SetProperty(ref _deviceName, value);
}
private bool _isConnected; private bool _isConnected;
public bool IsConnected public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
{
get => _isConnected;
set => SetProperty(ref _isConnected, value);
}
private bool _isBusy; private bool _isBusy;
/// <summary>正在执行设备命令时为 true,用于 UI 忙碌状态指示。</summary> public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
public bool IsBusy
#region
private double _voltage; public double Voltage { get => _voltage; set => SetProperty(ref _voltage, value); }
private double _current; public double Current { get => _current; set => SetProperty(ref _current, value); }
private double _voltageLimit; public double VoltageLimit { get => _voltageLimit; set => SetProperty(ref _voltageLimit, value); }
#endregion
#region
private double _measuredVoltage;
public double MeasuredVoltage { get => _measuredVoltage; set => SetProperty(ref _measuredVoltage, value); }
private double _measuredCurrent;
public double MeasuredCurrent { get => _measuredCurrent; set => SetProperty(ref _measuredCurrent, value); }
private string _outputMode = "";
public string OutputMode { get => _outputMode; set => SetProperty(ref _outputMode, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand OutOn { get; } public ICommand OutOff { get; }
public ICommand SetRemote { get; } public ICommand SetLocal { get; }
public ICommand SetV { get; } public ICommand SetI { get; } public ICommand SetVLim { get; }
public ICommand QMeas { get; } public ICommand QMode { get; }
#endregion
public IT6720ViewModel(IContainerProvider cp) : base(cp)
{ {
get => _isBusy; _dm = cp.Resolve<DeviceManager>();
set => SetProperty(ref _isBusy, value); QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.(Ct()))));
} OutOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.(true, Ct()); Log("输出已开启"); }));
private string _responseLog = string.Empty; OutOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.(false, Ct()); Log("输出已关闭"); }));
/// <summary>命令响应日志(最新消息在顶部)。</summary> SetRemote = new DelegateCommand(async () => await Exec(async () => { await _dev!.(true, Ct()); Log("远程控制"); }));
public string ResponseLog SetLocal = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("本地控制"); }));
{ SetV = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Voltage, Ct()); Log($"电压={Voltage}V"); }));
get => _responseLog; SetI = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Current, Ct()); Log($"电流={Current}A"); }));
set => SetProperty(ref _responseLog, value); SetVLim = new DelegateCommand(async () => await Exec(async () => { await _dev!.(VoltageLimit, Ct()); Log($"电压上限={VoltageLimit}V"); }));
QMeas = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVoltage = await _dev!.(Ct());
MeasuredCurrent = await _dev!.(Ct());
Log($"测量→V:{MeasuredVoltage} I:{MeasuredCurrent}");
}));
QMode = new DelegateCommand(async () => await Exec(async () => { OutputMode = await _dev!.(Ct()); Log($"输出模式={OutputMode}"); }));
Initialize();
} }
#endregion
#region
#endregion
public IT6720ViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#region / Navigation #region / Navigation
/// <summary>
/// 从 DeviceManager 中查找IT6720设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null) public void Initialize(string? deviceName = null)
{ {
IT6720? found = null; IT6720? found = null; string? fn = null;
string? foundName = null; if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is IT6720 e)
if (deviceName != null && { found = e; fn = deviceName; }
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is IT6720 e)
{
found = e;
foundName = deviceName;
}
else else
{ {
foreach (var kv in _deviceManager.DeviceMap) foreach (var kv in _dm.DeviceMap)
{ if (kv.Value is IT6720 it) { found = it; fn = kv.Key; break; }
if (kv.Value is IT6720 it)
{
found = it;
foundName = kv.Key;
break;
}
}
} }
_dev = found;
_device = found; DeviceName = fn ?? "IT6720 (未找到)";
DeviceName = foundName ?? "IT7800E (未找到)"; IsConnected = _dev?.IsConnected ?? false;
IsConnected = _device?.IsConnected ?? false; Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
AppendLog(found != null : "未在 DeviceManager 中找到 IT6720 设备");
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
} }
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region #region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token; private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action) private async Task Exec(Func<Task> action)
{ {
if (_device == null) if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return; if (IsBusy) return;
IsBusy = true; IsBusy = true;
try try
{ {
await action(); await action();
IsConnected = _device.IsConnected; IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
} }
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
} }
private void AppendLog(string message) private void Log(string message)
{ {
var line = $"[{DateTime.Now:HH:mm:ss}] {message}"; var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000 ResponseLog = ResponseLog.Length > 4000
@@ -136,12 +127,11 @@ namespace DeviceEditModule.ViewModels
} }
#endregion #endregion
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
} }
} }
@@ -0,0 +1,140 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// MCc30W 水冷机一拖三 控制面板 ViewModel
/// </summary>
public class MCc30WViewModel : NavigateViewModelBase, IDisposable
{
private readonly DeviceManager _dm;
private MCc30W? _dev;
private CancellationTokenSource? _cts;
private string _deviceName = "MCc30W";
public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
private bool _isConnected;
public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
private bool _isBusy;
public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
#region
private int _loop = 1; public int Loop { get => _loop; set => SetProperty(ref _loop, value); }
private float _tempSet = 25f; public float TempSet { get => _tempSet; set => SetProperty(ref _tempSet, value); }
private float _flowSet = 10f; public float FlowSet { get => _flowSet; set => SetProperty(ref _flowSet, value); }
private float _pressSet = 100f; public float PressSet { get => _pressSet; set => SetProperty(ref _pressSet, value); }
#endregion
#region
private float _currentTemp;
public float CurrentTemp { get => _currentTemp; set => SetProperty(ref _currentTemp, value); }
private float _currentFlow;
public float CurrentFlow { get => _currentFlow; set => SetProperty(ref _currentFlow, value); }
private float _currentPress;
public float CurrentPress { get => _currentPress; set => SetProperty(ref _currentPress, value); }
private uint _alarmInfo;
public uint AlarmInfo { get => _alarmInfo; set => SetProperty(ref _alarmInfo, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand LoopOn { get; } public ICommand LoopOff { get; }
public ICommand SetTemp { get; } public ICommand SetFlow { get; } public ICommand SetPress { get; }
public ICommand AlarmReset { get; } public ICommand AlarmSilence { get; }
public ICommand ReadAll { get; } public ICommand ReadAlarm { get; }
#endregion
public MCc30WViewModel(IContainerProvider cp) : base(cp)
{
_dm = cp.Resolve<DeviceManager>();
LoopOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Loop, true, Ct()); Log($"回路{Loop}已启动"); }));
LoopOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Loop, false, Ct()); Log($"回路{Loop}已停止"); }));
SetTemp = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Loop, TempSet, Ct()); Log($"回路{Loop}温度={TempSet}℃"); }));
SetFlow = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Loop, FlowSet, Ct()); Log($"回路{Loop}流量={FlowSet}L/min"); }));
SetPress = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(Loop, PressSet, Ct()); Log($"回路{Loop}压力={PressSet}kPa"); }));
AlarmReset = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(true, Ct()); await _dev!.Async(false, Ct()); Log("报警已复位"); }));
AlarmSilence = new DelegateCommand(async () => await Exec(async () => { await _dev!.Async(true, Ct()); Log("报警已消音"); }));
ReadAll = new DelegateCommand(async () => await Exec(async () =>
{
CurrentTemp = await _dev!.Async(Loop, Ct());
CurrentFlow = await _dev!.Async(Loop, Ct());
CurrentPress = await _dev!.Async(Loop, Ct());
Log($"回路{Loop}→T:{CurrentTemp}℃ F:{CurrentFlow}L/min P:{CurrentPress}kPa");
}));
ReadAlarm = new DelegateCommand(async () => await Exec(async () => { AlarmInfo = await _dev!.1Async(Ct()); Log($"报警信息={AlarmInfo}"); }));
Initialize();
}
#region / Navigation
public void Initialize(string? deviceName = null)
{
MCc30W? found = null; string? fn = null;
if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is MCc30W e)
{ found = e; fn = deviceName; }
else
{
foreach (var kv in _dm.DeviceMap)
if (kv.Value is MCc30W it) { found = it; fn = kv.Key; break; }
}
_dev = found;
DeviceName = fn ?? "MCc30W (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 MCc30W 设备");
}
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
private void Log(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
}
+92 -99
View File
@@ -1,134 +1,128 @@
using DeviceCommand.Device; using DeviceCommand.Device;
using DeviceCommand.Devices; using Prism.Commands;
using Prism.Ioc;
using System; using System;
using System.Collections.Generic; using System.Threading;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable; using UIShare.GlobalVariable;
using UIShare.ViewModelBase; using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels namespace DeviceEditModule.ViewModels
{ {
/// <summary>
/// PW8001 功率分析仪 控制面板 ViewModel
/// </summary>
public class PW8001ViewModel : NavigateViewModelBase, IDisposable public class PW8001ViewModel : NavigateViewModelBase, IDisposable
{ {
#region private readonly DeviceManager _dm;
private readonly DeviceManager _deviceManager; private PW8001? _dev;
private PW8001? _device;
private CancellationTokenSource? _cts; private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "PW8001"; private string _deviceName = "PW8001";
public string DeviceName public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
{
get => _deviceName;
set => SetProperty(ref _deviceName, value);
}
private bool _isConnected; private bool _isConnected;
public bool IsConnected public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
{
get => _isConnected;
set => SetProperty(ref _isConnected, value);
}
private bool _isBusy; private bool _isBusy;
/// <summary>正在执行设备命令时为 true,用于 UI 忙碌状态指示。</summary> public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
public bool IsBusy
#region
private int _channel = 1; public int Channel { get => _channel; set => SetProperty(ref _channel, value); }
#endregion
#region
private double _measuredVoltage;
public double MeasuredVoltage { get => _measuredVoltage; set => SetProperty(ref _measuredVoltage, value); }
private double _measuredCurrent;
public double MeasuredCurrent { get => _measuredCurrent; set => SetProperty(ref _measuredCurrent, value); }
private double _measuredPower;
public double MeasuredPower { get => _measuredPower; set => SetProperty(ref _measuredPower, value); }
private double _voltageTHD;
public double VoltageTHD { get => _voltageTHD; set => SetProperty(ref _voltageTHD, value); }
private double _currentTHD;
public double CurrentTHD { get => _currentTHD; set => SetProperty(ref _currentTHD, value); }
private double _totalPower;
public double TotalPower { get => _totalPower; set => SetProperty(ref _totalPower, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand Reset { get; } public ICommand Clear { get; }
public ICommand ModeWIDE { get; } public ICommand ModeIEC { get; }
public ICommand QV { get; } public ICommand QI { get; } public ICommand QP { get; }
public ICommand QP123 { get; } public ICommand QTHD { get; }
#endregion
public PW8001ViewModel(IContainerProvider cp) : base(cp)
{ {
get => _isBusy; _dm = cp.Resolve<DeviceManager>();
set => SetProperty(ref _isBusy, value); QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.(Ct()))));
} Reset = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("仪器已复位"); }));
private string _responseLog = string.Empty; Clear = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("状态已清除"); }));
/// <summary>命令响应日志(最新消息在顶部)。</summary> ModeWIDE = new DelegateCommand(async () => await Exec(async () => { await _dev!._WIDE(Ct()); Log("WIDE模式"); }));
public string ResponseLog ModeIEC = new DelegateCommand(async () => await Exec(async () => { await _dev!._IEC(Ct()); Log("IEC模式"); }));
{ QV = new DelegateCommand(async () => await Exec(async () => { MeasuredVoltage = await _dev!._不含变比(Channel, Ct()); Log($"CH{Channel}电压={MeasuredVoltage}V"); }));
get => _responseLog; QI = new DelegateCommand(async () => await Exec(async () => { MeasuredCurrent = await _dev!._不含变比(Channel, Ct()); Log($"CH{Channel}电流={MeasuredCurrent}A"); }));
set => SetProperty(ref _responseLog, value); QP = new DelegateCommand(async () => await Exec(async () => { MeasuredPower = await _dev!._不含变比(Channel, Ct()); Log($"CH{Channel}功率={MeasuredPower}W"); }));
QP123 = new DelegateCommand(async () => await Exec(async () => { TotalPower = await _dev!.P123(Ct()); Log($"三相总功率={TotalPower}W"); }));
QTHD = new DelegateCommand(async () => await Exec(async () =>
{
VoltageTHD = await _dev!.THD(Channel, Ct());
CurrentTHD = await _dev!.THD(Channel, Ct());
Log($"CH{Channel} THD→U:{VoltageTHD}% I:{CurrentTHD}%");
}));
Initialize();
} }
#endregion
#region
#endregion
public PW8001ViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#region / Navigation #region / Navigation
/// <summary>
/// 从 DeviceManager 中查找PW8001设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null) public void Initialize(string? deviceName = null)
{ {
PW8001? found = null; PW8001? found = null; string? fn = null;
string? foundName = null; if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is PW8001 e)
if (deviceName != null && { found = e; fn = deviceName; }
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is PW8001 e)
{
found = e;
foundName = deviceName;
}
else else
{ {
foreach (var kv in _deviceManager.DeviceMap) foreach (var kv in _dm.DeviceMap)
{ if (kv.Value is PW8001 it) { found = it; fn = kv.Key; break; }
if (kv.Value is PW8001 it)
{
found = it;
foundName = kv.Key;
break;
}
}
} }
_dev = found;
_device = found; DeviceName = fn ?? "PW8001 (未找到)";
DeviceName = foundName ?? "IT7800E (未找到)"; IsConnected = _dev?.IsConnected ?? false;
IsConnected = _device?.IsConnected ?? false; Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
AppendLog(found != null : "未在 DeviceManager 中找到 PW8001 设备");
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
} }
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region #region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token; private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action) private async Task Exec(Func<Task> action)
{ {
if (_device == null) if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return; if (IsBusy) return;
IsBusy = true; IsBusy = true;
try try
{ {
await action(); await action();
IsConnected = _device.IsConnected; IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
} }
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
} }
private void AppendLog(string message) private void Log(string message)
{ {
var line = $"[{DateTime.Now:HH:mm:ss}] {message}"; var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000 ResponseLog = ResponseLog.Length > 4000
@@ -137,12 +131,11 @@ namespace DeviceEditModule.ViewModels
} }
#endregion #endregion
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
} }
} }
@@ -0,0 +1,131 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// RLT1000 环境箱 控制面板 ViewModel
/// </summary>
public class RLT1000ViewModel : NavigateViewModelBase, IDisposable
{
private readonly DeviceManager _dm;
private RLT1000? _dev;
private CancellationTokenSource? _cts;
private string _deviceName = "RLT1000";
public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
private bool _isConnected;
public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
private bool _isBusy;
public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
#region
private float _targetTemp = 25f; public float TargetTemp { get => _targetTemp; set => SetProperty(ref _targetTemp, value); }
private float _targetHumidity = 50f; public float TargetHumidity { get => _targetHumidity; set => SetProperty(ref _targetHumidity, value); }
#endregion
#region
private double _currentTemp;
public double CurrentTemp { get => _currentTemp; set => SetProperty(ref _currentTemp, value); }
private double _currentHumidity;
public double CurrentHumidity { get => _currentHumidity; set => SetProperty(ref _currentHumidity, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand PowerOn { get; } public ICommand PowerOff { get; }
public ICommand SetRemote { get; } public ICommand SetLocal { get; }
public ICommand SetTemp { get; } public ICommand SetHumidity { get; }
public ICommand ReadAll { get; }
#endregion
public RLT1000ViewModel(IContainerProvider cp) : base(cp)
{
_dm = cp.Resolve<DeviceManager>();
PowerOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("环境箱已开机"); }));
PowerOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("环境箱已关机"); }));
SetRemote = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("远程控制"); }));
SetLocal = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("本地控制"); }));
SetTemp = new DelegateCommand(async () => await Exec(async () => { await _dev!.(TargetTemp, Ct()); Log($"温度={TargetTemp}℃"); }));
SetHumidity = new DelegateCommand(async () => await Exec(async () => { await _dev!.湿(TargetHumidity, Ct()); Log($"湿度={TargetHumidity}%RH"); }));
ReadAll = new DelegateCommand(async () => await Exec(async () =>
{
CurrentTemp = await _dev!.(Ct());
CurrentHumidity = await _dev!.湿(Ct());
Log($"环境→T:{CurrentTemp}℃ H:{CurrentHumidity}%RH");
}));
Initialize();
}
#region / Navigation
public void Initialize(string? deviceName = null)
{
RLT1000? found = null; string? fn = null;
if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is RLT1000 e)
{ found = e; fn = deviceName; }
else
{
foreach (var kv in _dm.DeviceMap)
if (kv.Value is RLT1000 it) { found = it; fn = kv.Key; break; }
}
_dev = found;
DeviceName = fn ?? "RLT1000 (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 RLT1000 设备");
}
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
private void Log(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
}
@@ -0,0 +1,144 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// S7200 高压直流源载一体机 控制面板 ViewModel
/// </summary>
public class S7200ViewModel : NavigateViewModelBase, IDisposable
{
private readonly DeviceManager _dm;
private S7200? _dev;
private CancellationTokenSource? _cts;
private string _deviceName = "S7200";
public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); }
private bool _isConnected;
public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); }
private bool _isBusy;
public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); }
#region
private double _voltage; public double Voltage { get => _voltage; set => SetProperty(ref _voltage, value); }
private double _current; public double Current { get => _current; set => SetProperty(ref _current, value); }
private double _cvPosI = 10; public double CvPosI { get => _cvPosI; set => SetProperty(ref _cvPosI, value); }
private double _cvNegI = -10; public double CvNegI { get => _cvNegI; set => SetProperty(ref _cvNegI, value); }
private double _ovpValue; public double OvpValue { get => _ovpValue; set => SetProperty(ref _ovpValue, value); }
private double _ocpValue; public double OcpValue { get => _ocpValue; set => SetProperty(ref _ocpValue, value); }
#endregion
#region
private double _measuredVoltage;
public double MeasuredVoltage { get => _measuredVoltage; set => SetProperty(ref _measuredVoltage, value); }
private double _measuredCurrent;
public double MeasuredCurrent { get => _measuredCurrent; set => SetProperty(ref _measuredCurrent, value); }
private double _measuredPower;
public double MeasuredPower { get => _measuredPower; set => SetProperty(ref _measuredPower, value); }
private string _responseLog = "";
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region
public ICommand QueryIdn { get; } public ICommand OutOn { get; } public ICommand OutOff { get; }
public ICommand SetRemote { get; } public ICommand ClearProt { get; }
public ICommand SetV { get; } public ICommand SetCC { get; } public ICommand SetCVPos { get; } public ICommand SetCVNeg { get; }
public ICommand SetOVP { get; } public ICommand SetOCP { get; }
public ICommand QMeas { get; }
#endregion
public S7200ViewModel(IContainerProvider cp) : base(cp)
{
_dm = cp.Resolve<DeviceManager>();
QueryIdn = new DelegateCommand(async () => await Exec(async () => Log("IDN:" + await _dev!.(Ct()))));
OutOn = new DelegateCommand(async () => await Exec(async () => { await _dev!.(true, Ct()); Log("输出已开启"); }));
OutOff = new DelegateCommand(async () => await Exec(async () => { await _dev!.(false, Ct()); Log("输出已关闭"); }));
SetRemote = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("远程控制"); }));
ClearProt = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Ct()); Log("保护已清除"); }));
SetV = new DelegateCommand(async () => await Exec(async () => { await _dev!.(Voltage, Ct()); Log($"电压={Voltage}V"); }));
SetCC = new DelegateCommand(async () => await Exec(async () => { await _dev!.CC模式电流(Current, Ct()); Log($"CC电流={Current}A"); }));
SetCVPos = new DelegateCommand(async () => await Exec(async () => { await _dev!.CV正向电流(CvPosI, Ct()); Log($"CV正向电流={CvPosI}A"); }));
SetCVNeg = new DelegateCommand(async () => await Exec(async () => { await _dev!.CV反向电流(CvNegI, Ct()); Log($"CV反向电流={CvNegI}A"); }));
SetOVP = new DelegateCommand(async () => await Exec(async () => { await _dev!.OVP电压(OvpValue, Ct()); Log($"OVP={OvpValue}V"); }));
SetOCP = new DelegateCommand(async () => await Exec(async () => { await _dev!.OCP电流(OcpValue, Ct()); Log($"OCP={OcpValue}A"); }));
QMeas = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVoltage = await _dev!.(Ct());
MeasuredCurrent = await _dev!.(Ct());
MeasuredPower = await _dev!.(Ct());
Log($"测量→V:{MeasuredVoltage} I:{MeasuredCurrent} P:{MeasuredPower}");
}));
Initialize();
}
#region / Navigation
public void Initialize(string? deviceName = null)
{
S7200? found = null; string? fn = null;
if (deviceName != null && _dm.DeviceMap.TryGetValue(deviceName, out var d) && d is S7200 e)
{ found = e; fn = deviceName; }
else
{
foreach (var kv in _dm.DeviceMap)
if (kv.Value is S7200 it) { found = it; fn = kv.Key; break; }
}
_dev = found;
DeviceName = fn ?? "S7200 (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 S7200 设备");
}
public override void OnNavigatedTo(NavigationContext context)
{
var pName = context.Parameters.GetValue<string?>("DeviceName");
Initialize(pName);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未关联到设备实例,请检查设备配置。"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消。"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
private void Log(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}
}
+171
View File
@@ -0,0 +1,171 @@
<UserControl x:Class="DeviceEditModule.Views.ANEVH80View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="LightningBolt" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="ANEVH 高压源载一体机"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="输出控制" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开启" Command="{Binding OutOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭" Command="{Binding OutOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="复位" Command="{Binding Reset}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="源模式参数" Margin="0,8,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Voltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetV}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Current, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetI}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="功率 (W)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Power, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetP}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="负载(SINK)参数" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="负载电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding SinkCurrent, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetSinkI}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="负载功率 (W)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding SinkPower, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetSinkP}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时测量" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredVoltage, Mode=OneWay}"/>
<TextBlock Text="V" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredCurrent, Mode=OneWay}"/>
<TextBlock Text="A" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="功率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredPower, Mode=OneWay}"/>
<TextBlock Text="W" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QMeas}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DeviceEditModule.Views
{
/// <summary>
/// ANEVH80View.xaml 的交互逻辑
/// </summary>
public partial class ANEVH80View : UserControl
{
public ANEVH80View()
{
InitializeComponent();
}
}
}
+166
View File
@@ -0,0 +1,166 @@
<UserControl x:Class="DeviceEditModule.Views.Chroma61800View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Flash" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="Chroma61800 交流源一拖三"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="输出控制" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开启" Command="{Binding OutOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭" Command="{Binding OutOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="复位" Command="{Binding Reset}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="相模式" Style="{StaticResource ParamLabel}"/>
<Button Content="三相" Command="{Binding SetThreePhase}" Style="{StaticResource CmdBtn}"/>
<Button Content="单相" Command="{Binding SetSinglePhase}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="参数设置" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Voltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetV}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="频率 (Hz)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Frequency, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetF}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="波形" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Waveform, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetWave}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时测量" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="交流电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredVoltage, Mode=OneWay}"/>
<TextBlock Text="V" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="交流电流" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredCurrent, Mode=OneWay}"/>
<TextBlock Text="A" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="频率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredFrequency, Mode=OneWay}"/>
<TextBlock Text="Hz" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="功率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredPower, Mode=OneWay}"/>
<TextBlock Text="W" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QMeas}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DeviceEditModule.Views
{
/// <summary>
/// Chroma61800View.xaml 的交互逻辑
/// </summary>
public partial class Chroma61800View : UserControl
{
public Chroma61800View()
{
InitializeComponent();
}
}
}
+161 -14
View File
@@ -1,15 +1,162 @@
<UserControl x:Class="DeviceEditModule.Views.DG1000ZView" <UserControl x:Class="DeviceEditModule.Views.DG1000ZView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare" xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d" mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False" prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860"> d:DesignHeight="760" d:DesignWidth="860">
<Grid> <UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Grid> </UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="RadioTower" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="DG1000Z 信号发生器一拖三"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="通道输出" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开启输出" Command="{Binding OutOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭输出" Command="{Binding OutOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="重置" Command="{Binding Reset}" Style="{StaticResource WarnBtn}"/>
<Button Content="查询错误" Command="{Binding QueryError}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="波形参数" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="通道号" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Channel, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetFreq}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="频率 (Hz)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Frequency, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetFreq}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="幅度 (Vpp)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Amplitude, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetAmp}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="偏移 (Vdc)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding OffsetVoltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetOffset}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="占空比 (%)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding DutyCycle, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetDuty}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="测量查询" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="测量频率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredFrequency, Mode=OneWay}"/>
<TextBlock Text="Hz" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="测量幅度" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredAmplitude, Mode=OneWay}"/>
<TextBlock Text="Vpp" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QueryCounter}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl> </UserControl>
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DeviceEditModule.Views namespace DeviceEditModule.Views
{ {
+155 -14
View File
@@ -1,15 +1,156 @@
<UserControl x:Class="DeviceEditModule.Views.IT6720View" <UserControl x:Class="DeviceEditModule.Views.IT6720View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare" xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d" mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False" prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860"> d:DesignHeight="760" d:DesignWidth="860">
<Grid> <UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Grid> </UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Flash" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="IT6720 低压电源一拖三"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="输出控制" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开启" Command="{Binding OutOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭" Command="{Binding OutOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="远程控制" Command="{Binding SetRemote}" Style="{StaticResource CmdBtn}"/>
<Button Content="本地控制" Command="{Binding SetLocal}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="参数设置" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Voltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetV}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Current, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetI}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压上限 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding VoltageLimit, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetVLim}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时测量" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="实际电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredVoltage, Mode=OneWay}"/>
<TextBlock Text="V" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="实际电流" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredCurrent, Mode=OneWay}"/>
<TextBlock Text="A" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="输出模式" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding OutputMode, Mode=OneWay}"/>
<TextBlock Text="" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QMeas}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl> </UserControl>
-13
View File
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DeviceEditModule.Views namespace DeviceEditModule.Views
{ {
+161
View File
@@ -0,0 +1,161 @@
<UserControl x:Class="DeviceEditModule.Views.MCc30WView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Water" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="MCc30W 水冷机一拖三"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="回路控制" Style="{StaticResource ParamLabel}"/>
<Button Content="启动回路" Command="{Binding LoopOn}" Style="{StaticResource CmdBtn}"/>
<Button Content="停止回路" Command="{Binding LoopOff}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="保护" Style="{StaticResource ParamLabel}"/>
<Button Content="报警复位" Command="{Binding AlarmReset}" Style="{StaticResource CmdBtn}"/>
<Button Content="报警消音" Command="{Binding AlarmSilence}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="回路设定" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="回路号" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Loop, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetTemp}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="温度 (℃)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding TempSet, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetTemp}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="流量 (L/min)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding FlowSet, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetFlow}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="压力 (kPa)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding PressSet, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetPress}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时状态" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="出液温度" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentTemp, Mode=OneWay}"/>
<TextBlock Text="℃" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="出液流量" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentFlow, Mode=OneWay}"/>
<TextBlock Text="L/min" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="出液压力" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentPress, Mode=OneWay}"/>
<TextBlock Text="kPa" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="报警信息" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding AlarmInfo, Mode=OneWay}"/>
<TextBlock Text="" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding ReadAll}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>
+15
View File
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DeviceEditModule.Views
{
/// <summary>
/// MCc30WView.xaml 的交互逻辑
/// </summary>
public partial class MCc30WView : UserControl
{
public MCc30WView()
{
InitializeComponent();
}
}
}
+153 -13
View File
@@ -1,15 +1,155 @@
<UserControl x:Class="DeviceEditModule.Views.PW8001View" <UserControl x:Class="DeviceEditModule.Views.PW8001View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare" xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d" mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False" prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860"> d:DesignHeight="760" d:DesignWidth="860">
<Grid> <UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Grid> </UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="ChartBar" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="PW8001 功率分析仪"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="复位" Command="{Binding Reset}" Style="{StaticResource CmdBtn}"/>
<Button Content="清除状态" Command="{Binding Clear}" Style="{StaticResource WarnBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="测试模式" Style="{StaticResource ParamLabel}"/>
<Button Content="WIDE" Command="{Binding ModeWIDE}" Style="{StaticResource CmdBtn}"/>
<Button Content="IEC" Command="{Binding ModeIEC}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="通道设置" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="通道号" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Channel, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding QV}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="测量查询" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredVoltage, Mode=OneWay}"/>
<TextBlock Text="V" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredCurrent, Mode=OneWay}"/>
<TextBlock Text="A" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="功率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredPower, Mode=OneWay}"/>
<TextBlock Text="W" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="三相总功率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding TotalPower, Mode=OneWay}"/>
<TextBlock Text="W" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压THD" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding VoltageTHD, Mode=OneWay}"/>
<TextBlock Text="%" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流THD" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentTHD, Mode=OneWay}"/>
<TextBlock Text="%" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QV}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl> </UserControl>
-13
View File
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DeviceEditModule.Views namespace DeviceEditModule.Views
{ {
+144
View File
@@ -0,0 +1,144 @@
<UserControl x:Class="DeviceEditModule.Views.RLT1000View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Thermostat" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="RLT1000 环境箱"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="电源控制" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开机" Command="{Binding PowerOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关机" Command="{Binding PowerOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="远程控制" Command="{Binding SetRemote}" Style="{StaticResource CmdBtn}"/>
<Button Content="本地控制" Command="{Binding SetLocal}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<GroupBox Header="定值设定" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="目标温度 (℃)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding TargetTemp, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetTemp}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="目标湿度 (%RH)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding TargetHumidity, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetHumidity}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时监测" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="当前温度" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentTemp, Mode=OneWay}"/>
<TextBlock Text="℃" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="当前湿度" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding CurrentHumidity, Mode=OneWay}"/>
<TextBlock Text="%RH" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding ReadAll}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DeviceEditModule.Views
{
/// <summary>
/// RLT1000View.xaml 的交互逻辑
/// </summary>
public partial class RLT1000View : UserControl
{
public RLT1000View()
{
InitializeComponent();
}
}
}
+178
View File
@@ -0,0 +1,178 @@
<UserControl x:Class="DeviceEditModule.Views.S7200View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="False"
d:DesignHeight="760" d:DesignWidth="860">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="12">
<materialDesign:Card Margin="0,0,0,8" Padding="12,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Flash" Width="22" Height="22"
Foreground="#1565C0" Margin="0,0,8,0"
VerticalAlignment="Center"/>
<TextBlock Text="S7200 高压直流源载一体机"
FontSize="15" FontWeight="Bold"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding DeviceName, StringFormat=' [{0}]'}"
FontSize="13" Foreground="#757575"
VerticalAlignment="Center" Margin="4,0,0,0"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="10" Height="10" CornerRadius="5" Margin="0,0,6,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Background" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<TextBlock VerticalAlignment="Center" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="未连接"/>
<Setter Property="Foreground" Value="#F44336"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Text" Value="已连接"/>
<Setter Property="Foreground" Value="#4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ProgressBar IsIndeterminate="True" Width="80" Height="4"
Margin="12,0,0,0"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
</Grid>
</materialDesign:Card>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,4,0">
<GroupBox Header="输出控制" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<Button Content="开启" Command="{Binding OutOn}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭" Command="{Binding OutOff}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统" Style="{StaticResource ParamLabel}"/>
<Button Content="远程控制" Command="{Binding SetRemote}" Style="{StaticResource CmdBtn}"/>
<Button Content="清除保护" Command="{Binding ClearProt}" Style="{StaticResource WarnBtn}"/>
</StackPanel>
<GroupBox Header="源模式参数" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Voltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetV}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="CC电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding Current, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetCC}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="CV正向电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding CvPosI, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetCVPos}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="CV反向电流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding CvNegI, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetCVNeg}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="保护设置" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="OVP (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding OvpValue, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetOVP}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="OCP (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
Text="{Binding OcpValue, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetOCP}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="4,0,0,0">
<GroupBox Header="实时测量" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredVoltage, Mode=OneWay}"/>
<TextBlock Text="V" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="电流" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredCurrent, Mode=OneWay}"/>
<TextBlock Text="A" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="功率" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
Text="{Binding MeasuredPower, Mode=OneWay}"/>
<TextBlock Text="W" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QMeas}" Style="{StaticResource CmdBtn}" HorizontalAlignment="Left" Margin="0,4,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Header="设备信息" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Orientation="Horizontal" Margin="4,8">
<Button Content="查询 IDN" Command="{Binding QueryIdn}" Style="{StaticResource CmdBtn}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="响应日志" Margin="0,0,0,8" materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<ScrollViewer Height="260" VerticalScrollBarVisibility="Auto">
<TextBox Text="{Binding ResponseLog, Mode=OneWay}"
IsReadOnly="True" TextWrapping="Wrap"
FontSize="11" FontFamily="Consolas"
Background="#FAFAFA" BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>
+15
View File
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace DeviceEditModule.Views
{
/// <summary>
/// S7200View.xaml 的交互逻辑
/// </summary>
public partial class S7200View : UserControl
{
public S7200View()
{
InitializeComponent();
}
}
}
+166
View File
@@ -82,19 +82,23 @@ namespace ExportModule.ViewModels
public ICommand LoadedCommand { get; } public ICommand LoadedCommand { get; }
public ICommand QueryCommand { get; } public ICommand QueryCommand { get; }
public ICommand ExportCommand { get; } public ICommand ExportCommand { get; }
public ICommand ExportReportCommand { get; }
#endregion #endregion
#region #region
private readonly ITestReportService _testReportService; private readonly ITestReportService _testReportService;
private readonly ITestCheckRecordService _testCheckRecordService;
#endregion #endregion
public ExportViewModel(IContainerProvider containerProvider) : base(containerProvider) public ExportViewModel(IContainerProvider containerProvider) : base(containerProvider)
{ {
_testReportService = containerProvider.Resolve<ITestReportService>(); _testReportService = containerProvider.Resolve<ITestReportService>();
_testCheckRecordService = containerProvider.Resolve<ITestCheckRecordService>();
LoadedCommand = new AsyncDelegateCommand(OnLoad); LoadedCommand = new AsyncDelegateCommand(OnLoad);
QueryCommand = new AsyncDelegateCommand(OnQuery); QueryCommand = new AsyncDelegateCommand(OnQuery);
ExportCommand = new AsyncDelegateCommand(OnExport); ExportCommand = new AsyncDelegateCommand(OnExport);
ExportReportCommand = new AsyncDelegateCommand(OnExportReportCommand);
} }
#region #region
@@ -184,6 +188,40 @@ namespace ExportModule.ViewModels
ShowInfoMessageBox($"导出完成,共 {entities.Count} 条步骤记录,已保存至:{dialog.FileName}", () => { }); ShowInfoMessageBox($"导出完成,共 {entities.Count} 条步骤记录,已保存至:{dialog.FileName}", () => { });
} }
/// <summary>
/// 导出测试报告:上层为各测试项(IsTestItem 子程序)的 PASS/NG 汇总,下层为每次 OKExpression 判断明细
/// </summary>
private async Task OnExportReportCommand()
{
if (SelectedTestReport == null)
{
ShowErrorMessageBox("请先在列表中选择一条测试记录。", () => { });
return;
}
StatusMessage = "正在查询测试项判断记录...";
var checkResult = await _testCheckRecordService.GetByTestRoundIdAsync(SelectedTestReport.TestRoundId);
if (!checkResult.IsSuccess || checkResult.Data == null || checkResult.Data.Count == 0)
{
ShowErrorMessageBox("未找到该测试记录的测试项判断数据(需将子程序标记为测试项后运行)。", () => { });
return;
}
var dialog = new SaveFileDialog
{
Filter = "Excel 工作簿 (*.xlsx)|*.xlsx|所有文件 (*.*)|*.*",
DefaultExt = ".xlsx",
FileName = $"测试报告_{SelectedTestReport.Scope}_{SelectedTestReport.StartTime:yyyyMMdd_HHmmss}.xlsx"
};
if (dialog.ShowDialog() != true) return;
var records = checkResult.Data;
StatusMessage = $"正在导出 {records.Count} 条测试项判断记录...";
await Task.Run(() => ExportReportToExcel(dialog.FileName, records, SelectedTestReport));
ShowInfoMessageBox($"导出完成,已保存至:{dialog.FileName}", () => { });
}
#endregion #endregion
#region #region
@@ -245,6 +283,134 @@ namespace ExportModule.ViewModels
workbook.SaveAs(filePath); workbook.SaveAs(filePath);
} }
/// <summary>
/// 测试报告导出:上层为各测试项(IsTestItem 子程序)的 PASS/NG 汇总,下层为每次 OKExpression 判断明细(含重复判断、判断时间、数据值)
/// </summary>
private static void ExportReportToExcel(string filePath, List<TestCheckRecordEntity> records, TestReportModel report)
{
using var workbook = new XLWorkbook();
var ws = workbook.Worksheets.Add("测试报告");
const int colCount = 6;
var passColor = XLColor.FromArgb(0xB2, 0xFF, 0xB2); // 通过 - 浅绿(与现有导出配色一致)
var ngColor = XLColor.FromArgb(0xFF, 0xB2, 0xB2); // 失败 - 浅红
var headerColor = XLColor.FromArgb(0xEC, 0xEF, 0xF4);
int row = 1;
// ===== 标题与信息行 =====
ws.Range(row, 1, row, colCount).Merge();
ws.Cell(row, 1).Value = "测 试 报 告";
ws.Range(row, 1, row, colCount).Style.Font.Bold = true;
ws.Range(row, 1, row, colCount).Style.Font.FontSize = 16;
ws.Range(row, 1, row, colCount).Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
row++;
ws.Cell(row, 1).Value = $"台架:{report.Scope ?? ""}";
ws.Cell(row, 3).Value = $"测试文件:{report.FileName ?? ""}";
row++;
ws.Cell(row, 1).Value = $"开始时间:{report.StartTime:yyyy-MM-dd HH:mm:ss}";
ws.Cell(row, 3).Value = $"结束时间:{report.EndTime:yyyy-MM-dd HH:mm:ss}";
row += 2;
// 按测试项分组(保持首次出现顺序)
var groups = records.GroupBy(r => r.TestItemName).ToList();
// ===== 上层:测试项汇总 =====
ws.Cell(row, 1).Value = "【测试项汇总】";
ws.Cell(row, 1).Style.Font.Bold = true;
row++;
string[] summaryHeaders = { "序号", "测试项", "判定结果", "执行次数", "判定次数", "NG次数" };
for (int c = 0; c < summaryHeaders.Length; c++)
ws.Cell(row, c + 1).Value = summaryHeaders[c];
var headerRange = ws.Range(row, 1, row, colCount);
headerRange.Style.Font.Bold = true;
headerRange.Style.Fill.BackgroundColor = headerColor;
headerRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
headerRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
row++;
int seq = 1;
foreach (var group in groups)
{
var summaries = group.Where(x => x.IsSummary).ToList();
var details = group.Where(x => !x.IsSummary).ToList();
bool overallPass = summaries.All(s => s.Pass) && details.All(d => d.Pass);
ws.Cell(row, 1).Value = seq++;
ws.Cell(row, 2).Value = group.Key;
ws.Cell(row, 3).Value = overallPass ? "PASS" : "NG";
ws.Cell(row, 4).Value = summaries.Count;
ws.Cell(row, 5).Value = details.Count;
ws.Cell(row, 6).Value = details.Count(d => !d.Pass);
var dataRange = ws.Range(row, 1, row, colCount);
dataRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
dataRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
ws.Cell(row, 3).Style.Font.Bold = true;
ws.Cell(row, 3).Style.Fill.BackgroundColor = overallPass ? passColor : ngColor;
row++;
}
row++;
// ===== 下层:判断明细 =====
ws.Cell(row, 1).Value = "【判断明细】";
ws.Cell(row, 1).Style.Font.Bold = true;
row++;
string[] detailHeaders = { "序号", "判定时间", "步骤名称", "OKExpression", "判定结果", "数据值" };
foreach (var group in groups)
{
var details = group.Where(x => !x.IsSummary).ToList();
bool overallPass = group.Where(x => x.IsSummary).All(s => s.Pass) && details.All(d => d.Pass);
// 测试项块标题行(合并单元格)
ws.Range(row, 1, row, colCount).Merge();
ws.Cell(row, 1).Value = $"■ 测试项:{group.Key}  总体结果:{(overallPass ? "PASS" : "NG")}";
ws.Cell(row, 1).Style.Font.Bold = true;
ws.Range(row, 1, row, colCount).Style.Fill.BackgroundColor = overallPass ? passColor : ngColor;
row++;
for (int c = 0; c < detailHeaders.Length; c++)
ws.Cell(row, c + 1).Value = detailHeaders[c];
var detailHeaderRange = ws.Range(row, 1, row, colCount);
detailHeaderRange.Style.Font.Bold = true;
detailHeaderRange.Style.Fill.BackgroundColor = headerColor;
detailHeaderRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
detailHeaderRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
row++;
if (details.Count == 0)
{
ws.Range(row, 1, row, colCount).Merge();
ws.Cell(row, 1).Value = "(无 OKExpression 判断,结果由步骤执行成败决定)";
row++;
}
int detailSeq = 1;
foreach (var d in details)
{
ws.Cell(row, 1).Value = detailSeq++;
ws.Cell(row, 2).Value = d.CreateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
ws.Cell(row, 3).Value = d.StepName ?? "";
ws.Cell(row, 4).Value = d.OKExpression ?? "";
ws.Cell(row, 5).Value = d.Pass ? "PASS" : "NG";
ws.Cell(row, 6).Value = d.Values ?? "";
var detailRange = ws.Range(row, 1, row, colCount);
detailRange.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
detailRange.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
ws.Cell(row, 5).Style.Font.Bold = true;
ws.Cell(row, 5).Style.Fill.BackgroundColor = d.Pass ? passColor : ngColor;
row++;
}
row++;
}
ws.Columns().AdjustToContents();
workbook.SaveAs(filePath);
}
#endregion #endregion
#region #region
+9 -2
View File
@@ -60,7 +60,7 @@
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- 开始日期 --> <!-- 开始日期 -->
@@ -116,12 +116,19 @@
Margin="4,0" Margin="4,0"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<Button Grid.Column="8" <Button Grid.Column="8"
Content="导出测试报告"
Command="{Binding ExportReportCommand}"
Style="{StaticResource MaterialDesignFlatButton}"
Padding="16,6"
Margin="4,0"
VerticalAlignment="Center"/>
<Button Grid.Column="9"
Content="加载全部" Content="加载全部"
Command="{Binding LoadedCommand}" Command="{Binding LoadedCommand}"
Style="{StaticResource MaterialDesignFlatButton}" Style="{StaticResource MaterialDesignFlatButton}"
Padding="16,6" Padding="16,6"
Margin="4,0" Margin="4,0"
VerticalAlignment="Center"/> VerticalAlignment="Center" />
</Grid> </Grid>
</Border> </Border>
@@ -217,10 +217,10 @@ namespace MainModule.ViewModels
(LogAreaVM as IDisposable)?.Dispose(); (LogAreaVM as IDisposable)?.Dispose();
(ParametersManagerVM as IDisposable)?.Dispose(); (ParametersManagerVM as IDisposable)?.Dispose();
_globalInfo.ContextDic?.Remove(TestStatus); _globalInfo.ContextDic?.TryRemove(TestStatus, out _);
_globalInfo.StepRunningDic?.Remove(TestStatus); _globalInfo.StepRunningDic?.TryRemove(TestStatus, out _);
_globalInfo.ConfigDic?.Remove(TestStatus); _globalInfo.ConfigDic?.TryRemove(TestStatus, out _);
_globalInfo.ScopeDic?.Remove(TestStatus); _globalInfo.ScopeDic?.TryRemove(TestStatus, out _);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -234,11 +234,23 @@ namespace MainModule.ViewModels
#region #region
private async Task OnLoad() private async Task OnLoad()
{ {
if (!IsInitialized) // 设备初始化期间仅对当前台架显示灰度遮罩层,初始化完成后关闭(finally 保证异常时也能关闭)
_eventAggregator.GetEvent<ScopeOverlayEvent>().Publish(new ScopeOverlayArgs { Scope = TestStatus, Show = true });
// 让遮罩层先完成渲染,再进入同步阻塞的设备初始化(泵送 Dispatcher 至 Render 优先级)
System.Windows.Application.Current?.Dispatcher.Invoke(() => { }, System.Windows.Threading.DispatcherPriority.Render);
try
{ {
await _deviceManager.ConnectAllDevices(); if (!IsInitialized)
IsInitialized = true; {
await _deviceManager.ConnectAllDevices();
IsInitialized = true;
}
} }
finally
{
_eventAggregator.GetEvent<ScopeOverlayEvent>().Publish(new ScopeOverlayArgs { Scope = TestStatus, Show = false });
}
} }
private void OnRefresh() private void OnRefresh()
@@ -263,10 +275,10 @@ namespace MainModule.ViewModels
if (navigationContext.Parameters.ContainsKey("Name")) if (navigationContext.Parameters.ContainsKey("Name"))
{ {
TestStatus = navigationContext.Parameters.GetValue<string>("Name"); TestStatus = navigationContext.Parameters.GetValue<string>("Name");
_globalInfo.ContextDic.Add(TestStatus, _scopedContext); _globalInfo.ContextDic.TryAdd(TestStatus, _scopedContext);
_globalInfo.StepRunningDic.Add(TestStatus, _stepRunning); _globalInfo.StepRunningDic.TryAdd(TestStatus, _stepRunning);
_globalInfo.ScopeDic.Add(TestStatus, _scope); _globalInfo.ScopeDic.TryAdd(TestStatus, _scope);
_globalInfo.ConfigDic.Add(TestStatus, _systemConfig); _globalInfo.ConfigDic.TryAdd(TestStatus, _systemConfig);
if(_systemConfig.DefaultProgramFilePath != null&&File.Exists(_systemConfig.DefaultProgramFilePath)) if(_systemConfig.DefaultProgramFilePath != null&&File.Exists(_systemConfig.DefaultProgramFilePath))
{ {
var filePath = _systemConfig.DefaultProgramFilePath; var filePath = _systemConfig.DefaultProgramFilePath;
+8 -8
View File
@@ -15,41 +15,41 @@ namespace MainModule.ViewModels
private bool IsInitialized = false; private bool IsInitialized = false;
private string _expandedCellName = string.Empty; private string _expandedCellName = string.Empty;
#endregion #endregion
#region #region
public bool KeepAlive => true; // 保持存活 public bool KeepAlive => true; // 保持存活
public string ExpandedCellName public string ExpandedCellName
{ {
get => _expandedCellName; get => _expandedCellName;
set => SetProperty(ref _expandedCellName, value); set => SetProperty(ref _expandedCellName, value);
} }
#endregion #endregion
#region #region
public ICommand LoadedCommand { get; set; } public ICommand LoadedCommand { get; set; }
#endregion #endregion
public MainViewModel(IContainerProvider containerProvider) : base(containerProvider) public MainViewModel(IContainerProvider containerProvider) : base(containerProvider)
{ {
LoadedCommand = new DelegateCommand(OnLoaded); LoadedCommand = new DelegateCommand(OnLoaded);
_eventAggregator.GetEvent<ExpandViewEvent>().Subscribe(OnCellExpandRequested); _eventAggregator.GetEvent<ExpandViewEvent>().Subscribe(OnCellExpandRequested);
} }
#region #region
private void OnLoaded() private void OnLoaded()
{ {
if (IsInitialized) return; if (IsInitialized) return;
for (int i = 1; i <= 9; i++) for (int i = 1; i <= 3; i++)
{ {
var parameters = new NavigationParameters { { "Name", $"TestCell{i}" } }; var parameters = new NavigationParameters { { "Name", $"TestCell{i}" } };
_regionManager.RequestNavigate($"TestCell{i}", "ProtocolStartView", parameters); _regionManager.RequestNavigate($"TestCell{i}", "ProtocolStartView", parameters);
} }
IsInitialized = true; IsInitialized = true;
} }
// 不再物理搬迁视图,只需修改一个字符串属性 ExpandedCellName // 不再物理搬迁视图,只需修改一个字符串属性 ExpandedCellName
// XAML 里每个单元的 Style.Triggers 会根据该值处理隐藏 / 跨越 3x3 // XAML 里每个单元的 Style.Triggers 会根据该值处理隐藏 / 跨越整行
private void OnCellExpandRequested(string cellName) private void OnCellExpandRequested(string cellName)
{ {
ExpandedCellName = cellName ?? string.Empty; ExpandedCellName = cellName ?? string.Empty;
@@ -128,6 +128,25 @@
</vs:ParametersManager.Style> </vs:ParametersManager.Style>
</vs:ParametersManager> </vs:ParametersManager>
<Border x:Name="Overlay"
Background="#40000000"
Visibility="Collapsed"
Panel.ZIndex="1"
Grid.RowSpan="2"
Grid.ColumnSpan="4">
<StackPanel Width="150"
VerticalAlignment="Center"
Margin="0 0 0 100">
<ProgressBar Width="80"
Height="80"
Margin="20"
IsIndeterminate="True"
Style="{StaticResource MaterialDesignCircularProgressBar}" />
<TextBlock FontSize="30"
Text="加载中......"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</Grid> </Grid>
</Border> </Border>
</UserControl> </UserControl>
+19 -2
View File
@@ -1,4 +1,6 @@
using System; using Prism.Events;
using MainModule.ViewModels;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -12,6 +14,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using UIShare.PubEvent;
namespace MainModule.Views namespace MainModule.Views
{ {
@@ -20,9 +23,23 @@ namespace MainModule.Views
/// </summary> /// </summary>
public partial class AutomatedTestingView : UserControl public partial class AutomatedTestingView : UserControl
{ {
public AutomatedTestingView() public AutomatedTestingView(IEventAggregator eventAggregator)
{ {
InitializeComponent(); InitializeComponent();
eventAggregator.GetEvent<OverlayEvent>().Subscribe(ShowOverlay);
// 台架级加载遮罩:仅台架名称匹配时响应,避免其他台架/主窗口同时变灰
eventAggregator.GetEvent<ScopeOverlayEvent>().Subscribe(ShowScopeOverlay);
}
private void ShowOverlay(bool arg)
{
Overlay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed;
}
private void ShowScopeOverlay(ScopeOverlayArgs args)
{
// 只处理属于当前台架的遮罩事件(TestStatus 在 OnNavigatedTo 时赋值)
if (DataContext is not AutomatedTestingViewModel vm || vm.TestStatus != args.Scope) return;
Overlay.Visibility = args.Show ? Visibility.Visible : Visibility.Collapsed;
} }
} }
} }
+7 -71
View File
@@ -24,75 +24,9 @@
<!-- 顶部的选项卡,切换 3 个台架 --> <!-- 顶部的选项卡,切换 3 个台架 -->
<TabControl Margin="5"> <TabControl Margin="5">
<!-- 标签页 1Inverse 参数 = 展开全屏(ExpandedCellName 非空)时隐藏标签页,不动主题样式 -->
<TabControl.Resources> <TabItem Header="台架 1 (Bench 1)"
<!-- 1. 去除 TabControl 原生的丑陋边框和灰色背景 --> Visibility="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}, ConverterParameter=Inverse}">
<Style TargetType="TabControl">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0,5,0,0"/>
</Style>
<!-- 2. 美化 TabItem 标签头 -->
<Style TargetType="TabItem">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="#8C8C8C"/>
<!-- 默认灰字 -->
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="25,12"/>
<!-- 撑大点击区域 -->
<Setter Property="Margin" Value="0,0,4,0"/>
<!-- 标签之间的间距 -->
<Setter Property="Cursor" Value="Hand"/>
<!-- 核心魔法:重写外观结构 -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<!-- 用一个带底部边框的 Border 包装文字 -->
<Border Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="Transparent"
BorderThickness="0,0,0,3"
CornerRadius="4,4,0,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<!-- 交互状态1:鼠标悬浮 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="#E6E6E6"/>
<Setter Property="Foreground" Value="#595959"/>
</Trigger>
<!-- 交互状态2:被选中 -->
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="White"/>
<!-- 选中时的底部指示条颜色(蓝色) -->
<Setter TargetName="Border" Property="BorderBrush" Value="#007ACC"/>
<!-- 选中时的字体颜色(蓝色) -->
<Setter Property="Foreground" Value="#007ACC"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<!-- !!!这是你原有的全屏隐藏逻辑!完美保留!!! -->
<DataTrigger Binding="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}}" Value="Visible">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
<!-- 标签页 1 -->
<TabItem Header="台架 1 (Bench 1)">
<!-- 加一个白底包裹,显得更像现代卡片 --> <!-- 加一个白底包裹,显得更像现代卡片 -->
<Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5"> <Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5">
<ContentControl prism:RegionManager.RegionName="TestCell1" /> <ContentControl prism:RegionManager.RegionName="TestCell1" />
@@ -100,14 +34,16 @@
</TabItem> </TabItem>
<!-- 标签页 2 --> <!-- 标签页 2 -->
<TabItem Header="台架 2 (Bench 2)"> <TabItem Header="台架 2 (Bench 2)"
Visibility="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}, ConverterParameter=Inverse}">
<Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5"> <Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5">
<ContentControl prism:RegionManager.RegionName="TestCell2" /> <ContentControl prism:RegionManager.RegionName="TestCell2" />
</Border> </Border>
</TabItem> </TabItem>
<!-- 标签页 3 --> <!-- 标签页 3 -->
<TabItem Header="台架 3 (Bench 3)"> <TabItem Header="台架 3 (Bench 3)"
Visibility="{Binding ExpandedCellName, Converter={StaticResource StringToVisibilityConverter}, ConverterParameter=Inverse}">
<Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5"> <Border Background="White" CornerRadius="0,4,4,4" Margin="0" BorderThickness="0" Padding="5">
<ContentControl prism:RegionManager.RegionName="TestCell3" /> <ContentControl prism:RegionManager.RegionName="TestCell3" />
</Border> </Border>
+53
View File
@@ -0,0 +1,53 @@
using SqlSugar;
using System;
namespace Model.Entity
{
/// <summary>
/// 测试项判断记录:运行过程中测试项(IsTestItem 子程序)范围内每一次 OKExpression 判断。
/// 同一次运行的所有记录共享同一个 TestRoundId,导出测试报告时按此 Guid 查询。
/// IsSummary=true 的行为该测试项单次执行的汇总(PASS/NG),IsSummary=false 的行为单次判断明细。
/// </summary>
public class TestCheckRecordEntity : BaseEntity
{
/// <summary>同一次运行的统一标识(StepRunning.TestRoundID</summary>
[SugarColumn(ColumnName = "TestRoundId", ColumnDescription = "运行轮次标识")]
public Guid TestRoundId { get; set; }
/// <summary>作用域/台架名称</summary>
[SugarColumn(ColumnName = "Scope", ColumnDescription = "台架名称")]
public string Scope { get; set; } = string.Empty;
/// <summary>测试项名称(当前ADP文件路径)</summary>
[SugarColumn(ColumnName = "FileName", ColumnDescription = "测试项名称")]
public string FileName { get; set; } = string.Empty;
/// <summary>测试项名称(IsTestItem 子程序步骤的名称)</summary>
[SugarColumn(ColumnName = "TestItemName", Length = 200)]
public string TestItemName { get; set; } = string.Empty;
/// <summary>发生判断的步骤名称</summary>
[SugarColumn(ColumnName = "StepName", Length = 200)]
public string StepName { get; set; } = string.Empty;
/// <summary>子程序嵌套深度(0=主程序)</summary>
[SugarColumn(ColumnName = "Depth")]
public int Depth { get; set; }
/// <summary>判断表达式</summary>
[SugarColumn(ColumnName = "OKExpression", Length = 1000, IsNullable = true)]
public string? OKExpression { get; set; }
/// <summary>判断结果(true=PASS / false=NG</summary>
[SugarColumn(ColumnName = "Pass")]
public bool Pass { get; set; }
/// <summary>表达式变量的实际取值(如 "电压=12.5; 电流=3.2; "</summary>
[SugarColumn(ColumnName = "Values", Length = 2000, IsNullable = true)]
public string? Values { get; set; }
/// <summary>是否为汇总行(每个测试项单次执行结束时写入一条)</summary>
[SugarColumn(ColumnName = "IsSummary")]
public bool IsSummary { get; set; }
}
}
+3
View File
@@ -12,6 +12,9 @@ namespace Model.Models
public bool IsUsed { get; set; } = true; public bool IsUsed { get; set; } = true;
/// <summary>是否测试项(仅子程序步骤可标记,运行时记录其范围内所有 OKExpression 判断)</summary>
public bool IsTestItem { get; set; } = false;
public int Index { get; set; } public int Index { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
@@ -182,6 +182,9 @@ namespace MonitorModule.ViewModels
try { _dbFlushTask.Wait(TimeSpan.FromSeconds(1.5)); } catch { } try { _dbFlushTask.Wait(TimeSpan.FromSeconds(1.5)); } catch { }
} }
// 从 CAN 广播器注销当前作用域配置(一拖三场景:工位销毁时清理注册表)
if (!string.IsNullOrEmpty(TestStatus))
_canSignalBroadcaster?.UnregisterScope(TestStatus);
// 3. 释放容器作用域 // 3. 释放容器作用域
_scope?.Dispose(); _scope?.Dispose();
@@ -335,6 +338,8 @@ namespace MonitorModule.ViewModels
// 4. 启动广播器(Discover + Start // 4. 启动广播器(Discover + Start
_broadcaster.Discover(); _broadcaster.Discover();
_broadcaster.Start(); _broadcaster.Start();
// 将当前工位的 CAN 配置注册到全局广播器(一拖三场景:每个工位各自的 DBC/ConfigurationList
_canSignalBroadcaster.RegisterScope(_systemConfig.Title, _systemConfig);
_canSignalBroadcaster.Discover(); _canSignalBroadcaster.Discover();
_canSignalBroadcaster.Start(); _canSignalBroadcaster.Start();
+39
View File
@@ -128,5 +128,44 @@ namespace ORM
throw new Exception("连接数据库失败"); throw new Exception("连接数据库失败");
} }
} }
/// <summary>
/// 归档过期数据库文件:将旧的 SQLite 文件重命名为 "SQL_归档_yyyyMMdd_HHmmss.db"
/// 后续 <see cref="CreateDatabaseAndCheckConnection"/> 会自动创建新的空数据库。
/// <para>
/// 必须在 <see cref="InitSqlite"/> 之后、<see cref="CreateDatabaseAndCheckConnection"/> 之前调用,
/// 此时连接字符串已就绪但数据库文件尚未被打开。
/// </para>
/// </summary>
/// <param name="retentionDays">数据库文件保留天数,默认 180 天(半年)</param>
public static void TryArchiveOldDatabase(int retentionDays = 180)
{
try
{
// 从连接字符串中提取文件路径(格式:Data Source=xxx;
string dbPath = DbConnectionString
.Replace("Data Source=", "", StringComparison.OrdinalIgnoreCase)
.TrimEnd(';');
if (!File.Exists(dbPath)) return;
var cutoff = DateTime.Now.AddDays(-retentionDays);
var fileInfo = new FileInfo(dbPath);
if (fileInfo.LastWriteTime < cutoff)
{
string archiveName = $"SQL_归档_{fileInfo.LastWriteTime:yyyyMMdd_HHmmss}.db";
string archivePath = Path.Combine(fileInfo.DirectoryName!, archiveName);
File.Move(dbPath, archivePath);
System.Diagnostics.Debug.WriteLine(
$"[数据库归档] {dbPath} → {archivePath}(文件最后修改于 {fileInfo.LastWriteTime:yyyy-MM-dd},已超过 {retentionDays} 天)");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"数据库归档失败(不影响软件正常使用):{ex.Message}");
}
}
} }
} }
@@ -0,0 +1,38 @@
using Model;
using Model.Entity;
using ORM;
using Service.Interface;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Service.Implement
{
public class TestCheckRecordService : BaseService<TestCheckRecordEntity>, ITestCheckRecordService
{
public TestCheckRecordService(SqlSugarRepository<TestCheckRecordEntity> repository) : base(repository)
{
}
/// <summary>
/// 根据 TestRoundId 查询该次运行的所有测试项判断记录(按创建时间升序)
/// </summary>
public async Task<Result<List<TestCheckRecordEntity>>> GetByTestRoundIdAsync(Guid testRoundId)
{
try
{
var list = await _repository.Entities
.Where(x => x.TestRoundId == testRoundId)
.OrderBy(x => x.CreateTime)
.ToListAsync();
return Result<List<TestCheckRecordEntity>>.Success(list);
}
catch (Exception ex)
{
return Result<List<TestCheckRecordEntity>>.Error("根据 TestRoundId 查询测试项判断记录失败", ex);
}
}
}
}
@@ -0,0 +1,19 @@
using Model;
using Model.Entity;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Service.Interface
{
/// <summary>
/// 测试项判断记录服务:运行时记录测试项范围内每一次 OKExpression 判断,导出测试报告时查询。
/// </summary>
public interface ITestCheckRecordService : IBaseService<TestCheckRecordEntity>
{
/// <summary>
/// 根据 TestRoundId 查询该次运行的所有测试项判断记录(按创建时间升序)
/// </summary>
Task<Result<List<TestCheckRecordEntity>>> GetByTestRoundIdAsync(Guid testRoundId);
}
}

Some files were not shown because too many files have changed in this diff Show More