Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac26ab255c | ||
|
|
60d788178f | ||
|
|
fa76bdb61c | ||
|
|
e7702405d8 | ||
|
|
2ddaf0e6f7 | ||
|
|
bd1c25a67d | ||
|
|
9c7c426507 | ||
|
|
11fe48ea9a | ||
|
|
18995ee0e2 | ||
|
|
d83843d2fd | ||
|
|
d88b81a8f0 | ||
|
|
d35e451c40 | ||
|
|
3b7a41d7f8 | ||
|
|
e8a7aa52aa | ||
|
|
c41190aa33 | ||
|
|
f46df16275 |
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(() =>
|
||||||
{
|
{
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 220 KiB |
|
Before Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 142 KiB |
@@ -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
|
|
||||||
|
|
||||||
@@ -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')}")
|
|
||||||
@@ -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()
|
|
||||||
@@ -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}")
|
|
||||||
@@ -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% 额定负载")
|
|
||||||
@@ -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(" - 输入电压下限点")
|
|
||||||
@@ -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']}")
|
|
||||||
@@ -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']}")
|
|
||||||
@@ -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']}")
|
|
||||||
@@ -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']}")
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
# ---- 四、测试点1:250Vdc ----
|
|
||||||
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)]))
|
|
||||||
|
|
||||||
# ---- 五、测试点2:300Vdc ----
|
|
||||||
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)]))
|
|
||||||
|
|
||||||
# ---- 六、测试点3:350Vdc ----
|
|
||||||
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)]))
|
|
||||||
|
|
||||||
# ---- 七、测试点4:400Vdc ----
|
|
||||||
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)]))
|
|
||||||
|
|
||||||
# ---- 八、测试点5:450Vdc ----
|
|
||||||
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)]))
|
|
||||||
|
|
||||||
# ---- 九、测试点6:500Vdc ----
|
|
||||||
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")
|
|
||||||
@@ -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规格填写输出电压上下限及对应的负载电流值")
|
|
||||||
@@ -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°")
|
|
||||||
@@ -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")
|
|
||||||
@@ -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']}")
|
|
||||||
@@ -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系统),请根据实际额定电压和限压阈值调整")
|
|
||||||
@@ -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%) ----
|
|
||||||
# 假设额定220V,5%不平衡=11V,L1=209V,L2=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系统),请根据实际额定电压调整")
|
|
||||||
@@ -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,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,17 +2,27 @@
|
|||||||
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>
|
||||||
/// ANEVH80 电子负载(一对三控制),基于 TCP 通信,使用 SCPI 指令集。
|
/// 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
|
||||||
@@ -29,15 +39,321 @@ namespace DeviceCommand.Devices
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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. 系统与公共指令
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除错误队列 (*CLS)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ct">异步取消令牌</param>
|
/// <param name="ct">异步取消令牌</param>
|
||||||
/// <returns>查询到的频率值,解析失败返回 0.0</returns>
|
public virtual async Task 清除错误队列Async(CancellationToken ct = default)
|
||||||
public virtual async Task<double> 占位符(CancellationToken ct = default)
|
|
||||||
{
|
{
|
||||||
string resp = await WriteReadAsync($"MEAS:FREQ?{ScpiDelimiter}", ScpiDelimiter, ct);
|
await SendAsync($"*CLS{ScpiDelimiter}", ct);
|
||||||
return double.TryParse(resp, NumberStyles.Any, CultureInfo.InvariantCulture, out double val) ? val : 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,166 @@ namespace DeviceCommand.Devices
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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> 读取回抽2流量目标值Async(CancellationToken ct = default)
|
public virtual async Task<float> 读取回抽2流量目标值Async(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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
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;
|
||||||
@@ -10,14 +11,18 @@ namespace DeviceCommand.Flexible
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 灵活型 Modbus RTU 串口通信类(免实例化,每次调用临时创建串口连接)。
|
/// 灵活型 Modbus RTU 串口通信类(免实例化,每次调用临时创建串口连接)。
|
||||||
/// 支持保持寄存器与线圈的读写操作,内部使用通信锁保证同一时刻只有一个事务在执行,
|
/// 支持保持寄存器与线圈的读写操作,内部使用按串口名称粒度的通信锁保证同一串口同一时刻只有一个事务在执行,
|
||||||
/// 适用于偶发性、无需保持长连接的 Modbus RTU 设备读写场景。
|
/// 不同串口之间互不阻塞,适用于偶发性、无需保持长连接的 Modbus RTU 设备读写场景。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ACPCommand]
|
[ACPCommand]
|
||||||
public static class FModbusRTU
|
public static class FModbusRTU
|
||||||
{
|
{
|
||||||
// 通信锁:保证同一时刻只有一个 Modbus 事务在执行
|
// 按串口名称粒度的通信锁:同一串口同一时刻只有一个事务在执行,不同串口互不阻塞
|
||||||
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>
|
||||||
/// 创建串口实例并配置超时参数。
|
/// 创建串口实例并配置超时参数。
|
||||||
@@ -76,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(
|
||||||
@@ -96,7 +101,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,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(
|
||||||
@@ -147,7 +152,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,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(
|
||||||
@@ -203,7 +208,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,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(
|
||||||
@@ -254,7 +259,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -10,14 +11,18 @@ namespace DeviceCommand.Flexible
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 灵活型 Modbus TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
|
/// 灵活型 Modbus TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
|
||||||
/// 支持保持寄存器与线圈的读写操作,内部使用通信锁保证同一时刻只有一个事务在执行,
|
/// 支持保持寄存器与线圈的读写操作,内部使用按端点粒度的通信锁保证同一端点同一时刻只有一个事务在执行,
|
||||||
/// 适用于偶发性、无需保持长连接的 Modbus TCP 设备读写场景。
|
/// 不同端点之间互不阻塞,适用于偶发性、无需保持长连接的 Modbus TCP 设备读写场景。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ACPCommand]
|
[ACPCommand]
|
||||||
public static class FModbusTCP
|
public static class FModbusTCP
|
||||||
{
|
{
|
||||||
// 通信锁:保证同一时刻只有一个 Modbus 事务在执行
|
// 按端点粒度的通信锁:同一端点同一时刻只有一个事务在执行,不同端点互不阻塞
|
||||||
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>
|
/// <summary>
|
||||||
/// 建立 TCP 连接并创建 Modbus TCP 主站。
|
/// 建立 TCP 连接并创建 Modbus TCP 主站。
|
||||||
@@ -57,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;
|
||||||
@@ -68,7 +73,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,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;
|
||||||
@@ -103,7 +108,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,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;
|
||||||
@@ -144,7 +149,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,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;
|
||||||
@@ -179,7 +184,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -10,14 +11,18 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 灵活型串口通信类(免实例化,每次调用临时创建串口连接)。
|
/// 灵活型串口通信类(免实例化,每次调用临时创建串口连接)。
|
||||||
/// 支持只发送指令、发送并读取应答两种最常用操作,
|
/// 支持只发送指令、发送并读取应答两种最常用操作,
|
||||||
/// 内部使用通信锁保证同一时刻只有一个串口事务在执行,
|
/// 内部使用按串口名称粒度的通信锁保证同一串口同一时刻只有一个事务在执行,
|
||||||
/// 适用于偶发性、无需保持长连接的串口设备通信场景(如示波器、电源的 SCPI 指令)。
|
/// 不同串口之间互不阻塞,适用于偶发性、无需保持长连接的串口设备通信场景(如示波器、电源的 SCPI 指令)。
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// 创建串口实例并配置 UTF8 编码与超时参数。
|
/// 创建串口实例并配置 UTF8 编码与超时参数。
|
||||||
@@ -48,7 +53,7 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <param name="ct">异步取消令牌</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)
|
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(
|
||||||
@@ -66,7 +71,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <returns>去除结束符并去除首尾空白后的应答字符串</returns>
|
/// <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)
|
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(
|
||||||
@@ -131,7 +136,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(portName).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Common.Attributes;
|
using Common.Attributes;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -7,14 +8,18 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 灵活型 TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
|
/// 灵活型 TCP 通信类(免实例化,每次调用临时建立 TCP 连接)。
|
||||||
/// 支持字节/文本发送、定长字节读取、按结束符读取文本行四种操作,
|
/// 支持字节/文本发送、定长字节读取、按结束符读取文本行四种操作,
|
||||||
/// 内部使用通信锁保证同一时刻只有一个 TCP 事务在执行,
|
/// 内部使用按端点粒度的通信锁保证同一端点同一时刻只有一个 TCP 事务在执行,
|
||||||
/// 适用于偶发性、无需保持长连接的 TCP 设备通信场景。
|
/// 不同端点之间互不阻塞,适用于偶发性、无需保持长连接的 TCP 设备通信场景。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ACPCommand]
|
[ACPCommand]
|
||||||
public static class FTCP
|
public static class FTCP
|
||||||
{
|
{
|
||||||
// 通信锁:保证同一时刻只有一个 TCP 事务在执行
|
// 按端点粒度的通信锁:同一端点同一时刻只有一个事务在执行,不同端点互不阻塞
|
||||||
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
|
||||||
|
|
||||||
@@ -28,7 +33,7 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <param name="ct">异步取消令牌</param>
|
/// <param name="ct">异步取消令牌</param>
|
||||||
public static async Task 发送字节数据(string ipAddress,int port,int sendTimeout, byte[] buffer, CancellationToken ct = default)
|
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();
|
||||||
@@ -41,7 +46,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +78,7 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <returns>实际读取到的字节数组(对端提前关闭时可能短于请求长度)</returns>
|
/// <returns>实际读取到的字节数组(对端提前关闭时可能短于请求长度)</returns>
|
||||||
public static async Task<byte[]> 读取字节数据(string ipAddress,int port,int receiveTimeout,int length,CancellationToken ct = default)
|
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();
|
||||||
@@ -100,7 +105,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +120,7 @@ namespace DeviceCommand.Flexible
|
|||||||
/// <returns>去除结束符并去除首尾空白后的文本行</returns>
|
/// <returns>去除结束符并去除首尾空白后的文本行</returns>
|
||||||
public static async Task<string> 读取文本行( string ipAddress, int port, int receiveTimeout, string delimiter = "\n",CancellationToken ct = default)
|
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();
|
||||||
@@ -147,7 +152,7 @@ namespace DeviceCommand.Flexible
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_commLock.Release();
|
GetLock(ipAddress, port).Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace DeviceEditModule.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MCc30WView.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class MCc30WView : UserControl
|
||||||
|
{
|
||||||
|
public MCc30WView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace DeviceEditModule.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// S7200View.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class S7200View : UserControl
|
||||||
|
{
|
||||||
|
public S7200View()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 生命周期
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace MainModule.ViewModels
|
|||||||
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);
|
||||||
@@ -49,7 +49,7 @@ namespace MainModule.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 不再物理搬迁视图,只需修改一个字符串属性 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>
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,75 +24,9 @@
|
|||||||
|
|
||||||
<!-- 顶部的选项卡,切换 3 个台架 -->
|
<!-- 顶部的选项卡,切换 3 个台架 -->
|
||||||
<TabControl Margin="5">
|
<TabControl Margin="5">
|
||||||
|
<!-- 标签页 1:Inverse 参数 = 展开全屏(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>
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,6 +97,7 @@ namespace TestingModule.ViewModels
|
|||||||
public ICommand SelectionChangedCommand { get;set; }
|
public ICommand SelectionChangedCommand { get;set; }
|
||||||
public ICommand OpenSubProgramCommand { get; set; }
|
public ICommand OpenSubProgramCommand { get; set; }
|
||||||
public ICommand GoBackCommand { get; set; }
|
public ICommand GoBackCommand { get; set; }
|
||||||
|
public ICommand ToggleTestItemCommand { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 子程序导航(主程序 / 错误程序各自独立)
|
#region 子程序导航(主程序 / 错误程序各自独立)
|
||||||
@@ -134,6 +135,7 @@ namespace TestingModule.ViewModels
|
|||||||
SelectionChangedCommand = new DelegateCommand<object>(SelectionChanged);
|
SelectionChangedCommand = new DelegateCommand<object>(SelectionChanged);
|
||||||
OpenSubProgramCommand = new DelegateCommand(OpenSubProgram);
|
OpenSubProgramCommand = new DelegateCommand(OpenSubProgram);
|
||||||
GoBackCommand = new DelegateCommand(GoBack);
|
GoBackCommand = new DelegateCommand(GoBack);
|
||||||
|
ToggleTestItemCommand = new DelegateCommand(ToggleTestItem);
|
||||||
SubscribeStepCollections();
|
SubscribeStepCollections();
|
||||||
Program.PropertyChanged += Program_PropertyChanged;
|
Program.PropertyChanged += Program_PropertyChanged;
|
||||||
Admin = _globalInfo.IsAdmin;
|
Admin = _globalInfo.IsAdmin;
|
||||||
@@ -298,6 +300,25 @@ namespace TestingModule.ViewModels
|
|||||||
nav.CurrentProgram = SelectedStep.SubProgram;
|
nav.CurrentProgram = SelectedStep.SubProgram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换选中步骤的测试项标记(仅子程序可标记,运行时将记录其范围内所有 OKExpression 判断)
|
||||||
|
/// </summary>
|
||||||
|
private void ToggleTestItem()
|
||||||
|
{
|
||||||
|
if (!_globalInfo.IsAdmin) return;
|
||||||
|
|
||||||
|
var source = (SelectedItems != null && SelectedItems.Any())
|
||||||
|
? SelectedItems
|
||||||
|
: (SelectedStep != null ? new List<StepVM> { SelectedStep } : null);
|
||||||
|
|
||||||
|
if (source == null || !source.Any()) return;
|
||||||
|
|
||||||
|
foreach (var item in source.Where(x => x.StepType == "子程序" && x.SubProgram != null))
|
||||||
|
{
|
||||||
|
item.IsTestItem = !item.IsTestItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回上一级程序(操作当前激活的 Tab 的导航状态)
|
/// 返回上一级程序(操作当前激活的 Tab 的导航状态)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="100" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="50" />
|
<ColumnDefinition Width="50" />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -240,7 +240,7 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Visibility="{Binding Category, Converter={StaticResource ParameterCategoryToVisibilityConverter}, ConverterParameter=Item}">
|
<Grid Visibility="{Binding Category, Converter={StaticResource ParameterCategoryToVisibilityConverter}, ConverterParameter=Item}">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="100" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="50" />
|
<ColumnDefinition Width="50" />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|||||||
@@ -53,124 +53,7 @@
|
|||||||
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
||||||
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||||
</i:EventTrigger>
|
</i:EventTrigger>
|
||||||
</i:Interaction.Triggers>
|
<i:EventTrigger EventName="ContextMenuOpening">
|
||||||
<!-- 行样式 -->
|
|
||||||
<DataGrid.RowStyle>
|
|
||||||
<Style TargetType="DataGridRow">
|
|
||||||
<Setter Property="Background"
|
|
||||||
Value="Transparent" />
|
|
||||||
<Setter Property="Foreground"
|
|
||||||
Value="Black" />
|
|
||||||
|
|
||||||
<Style.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding Result}"
|
|
||||||
Value="0">
|
|
||||||
<Setter Property="Background"
|
|
||||||
Value="DodgerBlue" />
|
|
||||||
<Setter Property="Foreground"
|
|
||||||
Value="White" />
|
|
||||||
</DataTrigger>
|
|
||||||
|
|
||||||
<DataTrigger Binding="{Binding Result}"
|
|
||||||
Value="1">
|
|
||||||
<Setter Property="Background"
|
|
||||||
Value="LimeGreen" />
|
|
||||||
<Setter Property="Foreground"
|
|
||||||
Value="White" />
|
|
||||||
</DataTrigger>
|
|
||||||
|
|
||||||
<DataTrigger Binding="{Binding Result}"
|
|
||||||
Value="2">
|
|
||||||
<Setter Property="Background"
|
|
||||||
Value="Red" />
|
|
||||||
<Setter Property="Foreground"
|
|
||||||
Value="White" />
|
|
||||||
</DataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.RowStyle>
|
|
||||||
|
|
||||||
<!-- 列 -->
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridCheckBoxColumn Width="58"
|
|
||||||
Binding="{Binding IsUsed, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
Header="启用" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Index}"
|
|
||||||
Header="序号"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Name}"
|
|
||||||
Header="名称"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding StepType}"
|
|
||||||
Header="类型"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Method.FullName}"
|
|
||||||
Header="指令类型"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Method.Name}"
|
|
||||||
Header="指令"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding OKExpression}"
|
|
||||||
Header="合格条件"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding RunTime}"
|
|
||||||
Header="耗时(ms)"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Result}"
|
|
||||||
Header="结果"
|
|
||||||
IsReadOnly="True" />
|
|
||||||
<DataGridTextColumn Binding="{Binding Description}"
|
|
||||||
Header="备注" />
|
|
||||||
</DataGrid.Columns>
|
|
||||||
|
|
||||||
<!-- ContextMenu MVVM化 -->
|
|
||||||
<DataGrid.ContextMenu>
|
|
||||||
<ContextMenu>
|
|
||||||
<MenuItem Header="编辑"
|
|
||||||
Command="{Binding EditStepCommand}" />
|
|
||||||
<MenuItem Header="复制"
|
|
||||||
Command="{Binding CopyStepCommand}" />
|
|
||||||
<MenuItem Header="粘贴"
|
|
||||||
Command="{Binding PasteStepCommand}" />
|
|
||||||
<MenuItem Header="删除"
|
|
||||||
Foreground="Red"
|
|
||||||
Command="{Binding DeleteStepCommand}" />
|
|
||||||
<Separator/>
|
|
||||||
<MenuItem Header="打开子程序"
|
|
||||||
Command="{Binding OpenSubProgramCommand}" />
|
|
||||||
</ContextMenu>
|
|
||||||
</DataGrid.ContextMenu>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</DataGrid>
|
|
||||||
</DockPanel>
|
|
||||||
</TabItem>
|
|
||||||
<TabItem Header="错误程序">
|
|
||||||
<DockPanel>
|
|
||||||
<!-- 错误程序面包屑导航栏 -->
|
|
||||||
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding ErrorCanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
|
|
||||||
<DockPanel>
|
|
||||||
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
|
|
||||||
Command="{Binding GoBackCommand}"
|
|
||||||
Style="{StaticResource MaterialDesignFlatButton}"
|
|
||||||
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
|
|
||||||
<TextBlock Text="{Binding ErrorBreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
<DataGrid dd:DragDrop.IsDragSource="True"
|
|
||||||
dd:DragDrop.IsDropTarget="True"
|
|
||||||
dd:DragDrop.UseDefaultDragAdorner="True"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
Background="Transparent"
|
|
||||||
CanUserAddRows="False"
|
|
||||||
CanUserSortColumns="False"
|
|
||||||
ItemsSource="{Binding ErrorDisplaySteps}"
|
|
||||||
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
SelectionMode="Extended"
|
|
||||||
SelectionUnit="FullRow">
|
|
||||||
<i:Interaction.Triggers>
|
|
||||||
<i:EventTrigger EventName="SelectionChanged">
|
|
||||||
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
||||||
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||||
</i:EventTrigger>
|
</i:EventTrigger>
|
||||||
@@ -216,6 +99,10 @@
|
|||||||
<DataGridCheckBoxColumn Width="58"
|
<DataGridCheckBoxColumn Width="58"
|
||||||
Binding="{Binding IsUsed, UpdateSourceTrigger=PropertyChanged}"
|
Binding="{Binding IsUsed, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Header="启用" />
|
Header="启用" />
|
||||||
|
<DataGridCheckBoxColumn Width="58"
|
||||||
|
Binding="{Binding IsTestItem}"
|
||||||
|
Header="测试项"
|
||||||
|
IsReadOnly="True" />
|
||||||
<DataGridTextColumn Binding="{Binding Index}"
|
<DataGridTextColumn Binding="{Binding Index}"
|
||||||
Header="序号"
|
Header="序号"
|
||||||
IsReadOnly="True" />
|
IsReadOnly="True" />
|
||||||
@@ -259,9 +146,151 @@
|
|||||||
<Separator/>
|
<Separator/>
|
||||||
<MenuItem Header="打开子程序"
|
<MenuItem Header="打开子程序"
|
||||||
Command="{Binding OpenSubProgramCommand}" />
|
Command="{Binding OpenSubProgramCommand}" />
|
||||||
|
<MenuItem Header="标记/取消测试项"
|
||||||
|
Command="{Binding ToggleTestItemCommand}" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</DataGrid>
|
||||||
|
</DockPanel>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="错误程序">
|
||||||
|
<DockPanel>
|
||||||
|
<!-- 错误程序面包屑导航栏 -->
|
||||||
|
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding ErrorCanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||||
|
<DockPanel>
|
||||||
|
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
|
||||||
|
Command="{Binding GoBackCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
|
||||||
|
<TextBlock Text="{Binding ErrorBreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
<DataGrid dd:DragDrop.IsDragSource="True"
|
||||||
|
dd:DragDrop.IsDropTarget="True"
|
||||||
|
dd:DragDrop.UseDefaultDragAdorner="True"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
Background="Transparent"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
ItemsSource="{Binding ErrorDisplaySteps}"
|
||||||
|
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
SelectionMode="Extended"
|
||||||
|
SelectionUnit="FullRow">
|
||||||
|
<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="SelectionChanged">
|
||||||
|
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
||||||
|
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
<i:EventTrigger EventName="ContextMenuOpening">
|
||||||
|
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
|
||||||
|
CommandParameter="{Binding SelectedItems, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>
|
||||||
|
<!-- 行样式 -->
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<Setter Property="Background"
|
||||||
|
Value="Transparent" />
|
||||||
|
<Setter Property="Foreground"
|
||||||
|
Value="Black" />
|
||||||
|
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Result}"
|
||||||
|
Value="0">
|
||||||
|
<Setter Property="Background"
|
||||||
|
Value="DodgerBlue" />
|
||||||
|
<Setter Property="Foreground"
|
||||||
|
Value="White" />
|
||||||
|
</DataTrigger>
|
||||||
|
|
||||||
|
<DataTrigger Binding="{Binding Result}"
|
||||||
|
Value="1">
|
||||||
|
<Setter Property="Background"
|
||||||
|
Value="LimeGreen" />
|
||||||
|
<Setter Property="Foreground"
|
||||||
|
Value="White" />
|
||||||
|
</DataTrigger>
|
||||||
|
|
||||||
|
<DataTrigger Binding="{Binding Result}"
|
||||||
|
Value="2">
|
||||||
|
<Setter Property="Background"
|
||||||
|
Value="Red" />
|
||||||
|
<Setter Property="Foreground"
|
||||||
|
Value="White" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
|
||||||
|
<!-- 列 -->
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridCheckBoxColumn Width="58"
|
||||||
|
Binding="{Binding IsUsed, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Header="启用" />
|
||||||
|
<DataGridCheckBoxColumn Width="58"
|
||||||
|
Binding="{Binding IsTestItem}"
|
||||||
|
Header="测试项"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Index}"
|
||||||
|
Header="序号"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Name}"
|
||||||
|
Header="名称"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding StepType}"
|
||||||
|
Header="类型"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Method.FullName}"
|
||||||
|
Header="指令类型"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Method.Name}"
|
||||||
|
Header="指令"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding OKExpression}"
|
||||||
|
Header="合格条件"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding RunTime}"
|
||||||
|
Header="耗时(ms)"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Result}"
|
||||||
|
Header="结果"
|
||||||
|
IsReadOnly="True" />
|
||||||
|
<DataGridTextColumn Binding="{Binding Description}"
|
||||||
|
Header="备注" />
|
||||||
|
</DataGrid.Columns>
|
||||||
|
|
||||||
|
<!-- ContextMenu MVVM化 -->
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="编辑"
|
||||||
|
Command="{Binding EditStepCommand}" />
|
||||||
|
<MenuItem Header="复制"
|
||||||
|
Command="{Binding CopyStepCommand}" />
|
||||||
|
<MenuItem Header="粘贴"
|
||||||
|
Command="{Binding PasteStepCommand}" />
|
||||||
|
<MenuItem Header="删除"
|
||||||
|
Foreground="Red"
|
||||||
|
Command="{Binding DeleteStepCommand}" />
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="打开子程序"
|
||||||
|
Command="{Binding OpenSubProgramCommand}" />
|
||||||
|
<MenuItem Header="标记/取消测试项"
|
||||||
|
Command="{Binding ToggleTestItemCommand}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
|
|
||||||
|
<!-- 键盘 删除键绑定 Delete 命令 -->
|
||||||
|
<!--<i:Interaction.Triggers>
|
||||||
|
<i:EventTrigger EventName="PreviewKeyDown">
|
||||||
|
<i:InvokeCommandAction Command="{Binding DeleteStepCommand}"
|
||||||
|
CommandParameter="{Binding SelectedStep}"
|
||||||
|
PassEventArgsToCommand="True" />
|
||||||
|
</i:EventTrigger>
|
||||||
|
</i:Interaction.Triggers>-->
|
||||||
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|||||||
@@ -13,18 +13,20 @@ namespace UIShare.Converters
|
|||||||
{
|
{
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
|
// 是否反转结果(字符串非空时返回 Collapsed),用于需要“有值则隐藏”的场景,如展开全屏时隐藏台架标签页
|
||||||
|
bool inverse = string.Equals(parameter?.ToString(), "Inverse", StringComparison.OrdinalIgnoreCase);
|
||||||
if(value is string str)
|
if(value is string str)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(str))
|
if (string.IsNullOrEmpty(str))
|
||||||
{
|
{
|
||||||
return Visibility.Collapsed;
|
return inverse ? Visibility.Visible : Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Visibility.Visible;
|
return inverse ? Visibility.Collapsed : Visibility.Visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Visibility.Collapsed;
|
return inverse ? Visibility.Visible : Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
|||||||
@@ -12,14 +12,23 @@ using UIShare.UIViewModel;
|
|||||||
namespace UIShare.GlobalVariable
|
namespace UIShare.GlobalVariable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CAN 信号广播器(全局单例):
|
/// CAN 信号广播器(全局单例,支持多作用域):
|
||||||
/// 不再订阅 CAN 实体实例的事件,改为从所有已注册的 <see cref="CANMonitoringService"/>
|
/// 从所有已注册的 <see cref="CANMonitoringService"/> 的 RealTimeSignals 字典中轮询读取信号值,
|
||||||
/// 的 RealTimeSignals 字典中轮询读取信号值,并通过 <see cref="HardwareDataReportedEvent"/> 广播。
|
/// 按每个已注册作用域的 ConfigurationList 分别映射,向对应作用域广播 HardwareDataReportedEvent。
|
||||||
|
/// <para>
|
||||||
|
/// 一拖三场景下 CAN 硬件共享,但各工位的 DBC/ConfigurationList 不同,
|
||||||
|
/// 因此广播器必须遍历所有已注册作用域的配置分别广播。
|
||||||
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CANSignalBroadcaster
|
public class CANSignalBroadcaster
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly SystemConfig _systemConfig;
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作用域注册表:scopeName → (SystemConfig, 预构建的 ConfigMap)。
|
||||||
|
/// 每个工位的 MonitorViewModel 在初始化时调用 <see cref="RegisterScope"/> 注册自己的配置。
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<string, (SystemConfig Config, Dictionary<string, CANSignalConfig> ConfigMap)> _scopeRegistry = new();
|
||||||
|
|
||||||
/// <summary>全局已注册的 CANMonitoringService 实例列表(静态,跨作用域共享)</summary>
|
/// <summary>全局已注册的 CANMonitoringService 实例列表(静态,跨作用域共享)</summary>
|
||||||
private static readonly ConcurrentDictionary<CANMonitoringService, byte> _registeredServices = new();
|
private static readonly ConcurrentDictionary<CANMonitoringService, byte> _registeredServices = new();
|
||||||
@@ -32,8 +41,9 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
public CANSignalBroadcaster(SystemConfig systemConfig, IEventAggregator eventAggregator)
|
public CANSignalBroadcaster(SystemConfig systemConfig, IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_systemConfig = systemConfig;
|
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
// 构造时自动注册第一个作用域(向后兼容)
|
||||||
|
RegisterScope(systemConfig.Title, systemConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 服务注册(供 CANMonitoringService 调用)
|
#region 服务注册(供 CANMonitoringService 调用)
|
||||||
@@ -70,6 +80,27 @@ namespace UIShare.GlobalVariable
|
|||||||
_broadcastTask = null;
|
_broadcastTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册一个作用域的 CAN 配置。
|
||||||
|
/// 每个工位在 MonitorViewModel 初始化时调用此方法,将自己的 SystemConfig 注册进来,
|
||||||
|
/// 广播器会在每轮广播中为该作用域独立映射信号并广播。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scopeName">作用域名称(SystemConfig.Title)</param>
|
||||||
|
/// <param name="config">该作用域的 SystemConfig</param>
|
||||||
|
public void RegisterScope(string scopeName, SystemConfig config)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(scopeName) || config == null) return;
|
||||||
|
var configMap = BuildConfigMap(config);
|
||||||
|
_scopeRegistry.AddOrUpdate(scopeName, (config, configMap), (_, _) => (config, configMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>注销一个作用域(工位销毁时调用)</summary>
|
||||||
|
public void UnregisterScope(string scopeName)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(scopeName))
|
||||||
|
_scopeRegistry.TryRemove(scopeName, out _);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>兼容旧接口:Discover 已无需执行任何操作</summary>
|
/// <summary>兼容旧接口:Discover 已无需执行任何操作</summary>
|
||||||
public void Discover() { }
|
public void Discover() { }
|
||||||
|
|
||||||
@@ -79,7 +110,7 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 轮询所有已注册的 CANMonitoringService 的 RealTimeSignals,
|
/// 轮询所有已注册的 CANMonitoringService 的 RealTimeSignals,
|
||||||
/// 结合 ConfigurationList 映射出 MessageID/Channel,广播 HardwareDataReportedEvent。
|
/// 遍历所有已注册作用域的配置分别映射并广播 HardwareDataReportedEvent。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task BroadcastLoop(CancellationToken ct)
|
private async Task BroadcastLoop(CancellationToken ct)
|
||||||
{
|
{
|
||||||
@@ -87,46 +118,53 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 预构建信号名 → 配置 的映射(避免内层循环重复查找)
|
// 快照当前作用域注册表,避免枚举期间被修改
|
||||||
var configMap = BuildConfigMap();
|
var scopeSnapshot = _scopeRegistry.ToArray();
|
||||||
|
|
||||||
string scope = _systemConfig.Title;
|
foreach (var scopeEntry in scopeSnapshot)
|
||||||
|
|
||||||
foreach (var service in _registeredServices.Keys)
|
|
||||||
{
|
{
|
||||||
if (service.IsStopped) continue;
|
if (ct.IsCancellationRequested) return;
|
||||||
|
|
||||||
foreach (var kvp in service.RealTimeSignals)
|
string scopeName = scopeEntry.Key;
|
||||||
|
var scopeConfig = scopeEntry.Value.Config;
|
||||||
|
var configMap = scopeEntry.Value.ConfigMap;
|
||||||
|
|
||||||
|
foreach (var service in _registeredServices.Keys)
|
||||||
{
|
{
|
||||||
if (ct.IsCancellationRequested) return;
|
if (service.IsStopped) continue;
|
||||||
|
|
||||||
// 信号 Key 格式: "{channel}/{MessageName}/{SignalName}"
|
foreach (var kvp in service.RealTimeSignals)
|
||||||
if (!TryParseSignalKey(kvp.Key, out int channel, out string? messageName, out string? signalName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 从配置映射中查找对应的 MessageID
|
|
||||||
if (!configMap.TryGetValue($"{channel}/{messageName}/{signalName}", out var cfg))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string canFingerprint = $"CAN:{channel}";
|
|
||||||
string fingerprint = BuildFingerprint(canFingerprint, (uint)channel);
|
|
||||||
string methodName = BuildMethodName((uint)cfg.MessageID, signalName);
|
|
||||||
|
|
||||||
// 广播信号值
|
|
||||||
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
|
||||||
{
|
{
|
||||||
Scope = scope,
|
if (ct.IsCancellationRequested) return;
|
||||||
HardwareFingerprint = fingerprint,
|
|
||||||
MethodName = methodName,
|
|
||||||
Value = kvp.Value,
|
|
||||||
Time = DateTime.Now
|
|
||||||
});
|
|
||||||
|
|
||||||
// 报警检查
|
// 信号 Key 格式: "{channel}/{MessageName}/{SignalName}"
|
||||||
string alarmStatus = ValueLimitAlarmHelper.CheckAlarm(fingerprint, methodName, kvp.Value, _systemConfig);
|
if (!TryParseSignalKey(kvp.Key, out int channel, out string? messageName, out string? signalName))
|
||||||
if (!string.IsNullOrEmpty(alarmStatus) && alarmStatus != "未报警")
|
continue;
|
||||||
{
|
|
||||||
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope, canFingerprint, alarmStatus));
|
// 从该作用域的配置映射中查找对应的 MessageID
|
||||||
|
if (!configMap.TryGetValue($"{channel}/{messageName}/{signalName}", out var cfg))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string canFingerprint = $"CAN:{channel}";
|
||||||
|
string fingerprint = BuildFingerprint(canFingerprint, (uint)channel);
|
||||||
|
string methodName = BuildMethodName((uint)cfg.MessageID, signalName);
|
||||||
|
|
||||||
|
// 广播信号值到对应作用域
|
||||||
|
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
||||||
|
{
|
||||||
|
Scope = scopeName,
|
||||||
|
HardwareFingerprint = fingerprint,
|
||||||
|
MethodName = methodName,
|
||||||
|
Value = kvp.Value,
|
||||||
|
Time = DateTime.Now
|
||||||
|
});
|
||||||
|
|
||||||
|
// 报警检查(使用该作用域自己的配置)
|
||||||
|
string alarmStatus = ValueLimitAlarmHelper.CheckAlarm(fingerprint, methodName, kvp.Value, scopeConfig);
|
||||||
|
if (!string.IsNullOrEmpty(alarmStatus) && alarmStatus != "未报警")
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<AlarmEvent>().Publish((scopeName, canFingerprint, alarmStatus));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +175,7 @@ namespace UIShare.GlobalVariable
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LoggerHelper.Error($"CANSignalBroadcaster 广播异常: {ex.Message}");
|
Logger.LoggerHelper.Error($"CANSignalBroadcaster 广播异常: {ex.Message}");
|
||||||
await Task.Delay(1000, ct); // 异常后等待一段时间再重试
|
await Task.Delay(1000, ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,12 +183,12 @@ namespace UIShare.GlobalVariable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从 SystemConfig.ConfigurationList 构建 "channel/MessageName/SignalName" → CANSignalConfig 的映射
|
/// 从 SystemConfig.ConfigurationList 构建 "channel/MessageName/SignalName" → CANSignalConfig 的映射
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<string, CANSignalConfig> BuildConfigMap()
|
private static Dictionary<string, CANSignalConfig> BuildConfigMap(SystemConfig systemConfig)
|
||||||
{
|
{
|
||||||
var map = new Dictionary<string, CANSignalConfig>(StringComparer.OrdinalIgnoreCase);
|
var map = new Dictionary<string, CANSignalConfig>(StringComparer.OrdinalIgnoreCase);
|
||||||
if (_systemConfig?.ConfigurationList == null) return map;
|
if (systemConfig?.ConfigurationList == null) return map;
|
||||||
|
|
||||||
foreach (var cfg in _systemConfig.ConfigurationList)
|
foreach (var cfg in systemConfig.ConfigurationList)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(cfg.SignalName) || string.IsNullOrEmpty(cfg.MessageName)) continue;
|
if (string.IsNullOrEmpty(cfg.SignalName) || string.IsNullOrEmpty(cfg.MessageName)) continue;
|
||||||
string key = $"{cfg.Channel}/{cfg.MessageName}/{cfg.SignalName}";
|
string key = $"{cfg.Channel}/{cfg.MessageName}/{cfg.SignalName}";
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
using DeviceCommand.Base;
|
||||||
|
using Logger;
|
||||||
|
using Model.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace UIShare.GlobalVariable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设备健康监控器:独立于监控采样的心跳重连机制。
|
||||||
|
/// <para>
|
||||||
|
/// 设计原则——与检测值零冲突:
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item>健康检查平时只读 <see cref="IBaseInterface.IsConnected"/>(纯本地属性,零网络开销,不碰通信锁)</item>
|
||||||
|
/// <item>仅在 IsConnected==false 时才获取 _commLock 执行重连,与监控采样天然串行化</item>
|
||||||
|
/// <item>ModbusTcp 的 ConnectAsync 是幂等的(已连接时直接返回),不会中断正在进行的监控</item>
|
||||||
|
/// <item>TCP 的 ConnectAsync 会重置连接,但监控的 catch 容忍单次失败,下次 tick 自动恢复</item>
|
||||||
|
/// </list>
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public class DeviceHealthMonitor : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IDictionary<string, IBaseInterface> _deviceMap;
|
||||||
|
private readonly SystemConfig _systemConfig;
|
||||||
|
private readonly string _scopeName;
|
||||||
|
|
||||||
|
/// <summary>健康检查定时器</summary>
|
||||||
|
private Timer? _healthCheckTimer;
|
||||||
|
|
||||||
|
/// <summary>每个设备的连续失败计数</summary>
|
||||||
|
private readonly ConcurrentDictionary<string, int> _failureCounts = new();
|
||||||
|
|
||||||
|
/// <summary>每个设备的重连尝试次数(用于计算退避阈值)</summary>
|
||||||
|
private readonly ConcurrentDictionary<string, int> _reconnectAttempts = new();
|
||||||
|
|
||||||
|
/// <summary>基础失败阈值(首次重连触发值)</summary>
|
||||||
|
private const int BaseFailureThreshold = 3;
|
||||||
|
|
||||||
|
/// <summary>退避上限(最大阈值)</summary>
|
||||||
|
private const int MaxBackoffThreshold = 15;
|
||||||
|
|
||||||
|
/// <summary>健康检查间隔(毫秒)</summary>
|
||||||
|
private readonly int _checkIntervalMs;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
private readonly object _startStopLock = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建健康监控器实例。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deviceMap">当前作用域的设备字典</param>
|
||||||
|
/// <param name="systemConfig">当前作用域的系统配置(用于更新 DeviceInfoVM.IsConnected)</param>
|
||||||
|
/// <param name="scopeName">作用域名称(日志标识)</param>
|
||||||
|
/// <param name="checkIntervalMs">健康检查间隔,默认 5000ms</param>
|
||||||
|
public DeviceHealthMonitor(
|
||||||
|
IDictionary<string, IBaseInterface> deviceMap,
|
||||||
|
SystemConfig systemConfig,
|
||||||
|
string scopeName,
|
||||||
|
int checkIntervalMs = 5000)
|
||||||
|
{
|
||||||
|
_deviceMap = deviceMap;
|
||||||
|
_systemConfig = systemConfig;
|
||||||
|
_scopeName = scopeName;
|
||||||
|
_checkIntervalMs = checkIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>启动健康监控(幂等:多次调用只启动一次)</summary>
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
lock (_startStopLock)
|
||||||
|
{
|
||||||
|
if (_disposed || _healthCheckTimer != null) return;
|
||||||
|
_healthCheckTimer = new Timer(
|
||||||
|
OnHealthCheckTick,
|
||||||
|
null,
|
||||||
|
TimeSpan.FromSeconds(10), // 首次检查延迟 10 秒,避免启动时设备尚未连接完成
|
||||||
|
TimeSpan.FromMilliseconds(_checkIntervalMs));
|
||||||
|
LoggerHelper.Info($"[{_scopeName}] 设备健康监控已启动,检查间隔={_checkIntervalMs}ms,基础重连阈值={BaseFailureThreshold}次(指数退避上限={MaxBackoffThreshold})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>停止健康监控</summary>
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
lock (_startStopLock)
|
||||||
|
{
|
||||||
|
_healthCheckTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
||||||
|
_healthCheckTimer?.Dispose();
|
||||||
|
_healthCheckTimer = null;
|
||||||
|
_failureCounts.Clear();
|
||||||
|
_reconnectAttempts.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 健康检查核心逻辑:遍历设备,检查连接状态,失败计数超阈值则重连。
|
||||||
|
/// <para>TCP 设备额外进行主动探活(*IDN?),以检测死连接(对端崩溃但 TCP 未收到 FIN)。</para>
|
||||||
|
/// </summary>
|
||||||
|
private async void OnHealthCheckTick(object? state)
|
||||||
|
{
|
||||||
|
if (_disposed || _deviceMap.Count == 0) return;
|
||||||
|
|
||||||
|
// 快照避免枚举期间字典被修改
|
||||||
|
var snapshot = _deviceMap.ToArray();
|
||||||
|
|
||||||
|
foreach (var kvp in snapshot)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
string deviceName = kvp.Key;
|
||||||
|
var device = kvp.Value;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool alive = device.IsConnected;
|
||||||
|
|
||||||
|
// TCP 设备主动探活:IsConnected 只反映上次操作状态,无法检测死连接
|
||||||
|
if (alive && device is Tcp tcpDevice)
|
||||||
|
{
|
||||||
|
alive = await ProbeTcpDeviceAsync(tcpDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alive)
|
||||||
|
{
|
||||||
|
// 连接正常:清零失败计数与退避
|
||||||
|
_failureCounts.TryRemove(deviceName, out _);
|
||||||
|
_reconnectAttempts.TryRemove(deviceName, out _);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接断开:累加失败计数
|
||||||
|
int failures = _failureCounts.AddOrUpdate(deviceName, 1, (_, count) => count + 1);
|
||||||
|
|
||||||
|
// 计算当前退避阈值:基础值 + 重连尝试次数 × 2,上限为 MaxBackoffThreshold
|
||||||
|
int attempts = _reconnectAttempts.GetOrAdd(deviceName, 0);
|
||||||
|
int currentThreshold = Math.Min(BaseFailureThreshold + attempts * 2, MaxBackoffThreshold);
|
||||||
|
|
||||||
|
if (failures < currentThreshold)
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn(
|
||||||
|
$"[{_scopeName}] 设备 [{deviceName}] 连接断开,等待重连中 ({failures}/{currentThreshold})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 达到阈值:执行重连
|
||||||
|
await ReconnectDeviceAsync(deviceName, device);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.Error($"[{_scopeName}] 设备 [{deviceName}] 健康检查异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对 TCP 设备执行轻量级主动探活(SCPI *IDN?),2 秒超时。
|
||||||
|
/// <para>通过 WriteReadAsync 内部获取 _commLock,与监控采样天然串行化。</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true: 探活成功(连接确实存活); false: 探活失败(死连接)</returns>
|
||||||
|
private async Task<bool> ProbeTcpDeviceAsync(Tcp tcpDevice)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var probeCts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
|
||||||
|
string resp = await tcpDevice.WriteReadAsync("*IDN?\n", "\n", probeCts.Token);
|
||||||
|
return !string.IsNullOrWhiteSpace(resp);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重连单个设备。
|
||||||
|
/// <para>
|
||||||
|
/// 冲突避免机制:ConnectAsync 内部获取设备的 _commLock,
|
||||||
|
/// 如果此时监控采样正在通信,重连会等待锁释放后再执行,
|
||||||
|
/// 保证同一时刻只有一个操作在使用通信链路。
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
private async Task ReconnectDeviceAsync(string deviceName, IBaseInterface device)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int attempts = _reconnectAttempts.AddOrUpdate(deviceName, 1, (_, c) => c + 1);
|
||||||
|
int nextThreshold = Math.Min(BaseFailureThreshold + attempts * 2, MaxBackoffThreshold);
|
||||||
|
LoggerHelper.Info($"[{_scopeName}] 设备 [{deviceName}] 连续失败触发重连(第 {attempts} 次重连,下次阈值={nextThreshold})...");
|
||||||
|
|
||||||
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||||
|
bool ok = await device.ConnectAsync(cts.Token);
|
||||||
|
|
||||||
|
// 同步更新 DeviceInfoVM 的 UI 状态
|
||||||
|
UpdateDeviceInfoState(deviceName, ok);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
_failureCounts.TryRemove(deviceName, out _);
|
||||||
|
_reconnectAttempts.TryRemove(deviceName, out _);
|
||||||
|
LoggerHelper.Info($"[{_scopeName}] 设备 [{deviceName}] 重连成功");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoggerHelper.Warn($"[{_scopeName}] 设备 [{deviceName}] 重连失败,将在下次检查时重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
UpdateDeviceInfoState(deviceName, false);
|
||||||
|
LoggerHelper.Warn($"[{_scopeName}] 设备 [{deviceName}] 重连超时(10s),将在下次检查时重试");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
UpdateDeviceInfoState(deviceName, false);
|
||||||
|
LoggerHelper.Error($"[{_scopeName}] 设备 [{deviceName}] 重连异常:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过 UI 线程更新 SystemConfig.DeviceList 中对应设备的 IsConnected 状态(驱动 UI 刷新)。
|
||||||
|
/// <para>Timer 回调运行在线程池线程上,必须切回 UI 线程才能触发 PropertyChanged。</para>
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateDeviceInfoState(string deviceName, bool isConnected)
|
||||||
|
{
|
||||||
|
var info = _systemConfig?.DeviceList?
|
||||||
|
.FirstOrDefault(d => d != null &&
|
||||||
|
string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (info == null) return;
|
||||||
|
|
||||||
|
var dispatcher = Application.Current?.Dispatcher;
|
||||||
|
if (dispatcher == null || dispatcher.CheckAccess())
|
||||||
|
{
|
||||||
|
// 已在 UI 线程(或无 Dispatcher),直接赋值
|
||||||
|
info.IsConnected = isConnected;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 切回 UI 线程赋值,避免跨线程 PropertyChanged 异常
|
||||||
|
dispatcher.BeginInvoke(() => info.IsConnected = isConnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ namespace UIShare.GlobalVariable
|
|||||||
private readonly string _scopeName;
|
private readonly string _scopeName;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
|
||||||
|
/// <summary>设备健康监控器:独立心跳检测 + 自动重连,与监控采样互不冲突</summary>
|
||||||
|
private DeviceHealthMonitor? _healthMonitor;
|
||||||
|
|
||||||
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
|
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
|
||||||
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
|
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
|
||||||
= new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
|
= new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -209,6 +212,30 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
// 所有设备连接完成后,启动健康监控(心跳重连)
|
||||||
|
StartHealthMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动设备健康监控器:周期性检查设备连接状态,断连自动重连。
|
||||||
|
/// 与 HardwareDataBroadcaster 的监控采样互不干扰。
|
||||||
|
/// </summary>
|
||||||
|
private void StartHealthMonitor()
|
||||||
|
{
|
||||||
|
if (_healthMonitor != null || DeviceMap.Count == 0) return;
|
||||||
|
|
||||||
|
_healthMonitor = new DeviceHealthMonitor(DeviceMap, _systemConfig, _scopeName);
|
||||||
|
_healthMonitor.Start();
|
||||||
|
LoggerHelper.Info($"[{_scopeName}] 心跳重连机制已激活");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>停止设备健康监控器</summary>
|
||||||
|
private void StopHealthMonitor()
|
||||||
|
{
|
||||||
|
_healthMonitor?.Stop();
|
||||||
|
_healthMonitor?.Dispose();
|
||||||
|
_healthMonitor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -277,6 +304,9 @@ namespace UIShare.GlobalVariable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task CloseAllDevicesAsync()
|
public async Task CloseAllDevicesAsync()
|
||||||
{
|
{
|
||||||
|
// 先停止健康监控,避免重连定时器与关闭操作冲突
|
||||||
|
StopHealthMonitor();
|
||||||
|
|
||||||
List<Task> tasks = new List<Task>();
|
List<Task> tasks = new List<Task>();
|
||||||
|
|
||||||
lock (_lockObj)
|
lock (_lockObj)
|
||||||
@@ -523,6 +553,9 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
// 停止健康监控
|
||||||
|
StopHealthMonitor();
|
||||||
|
|
||||||
// 停止 CAN 信号监测服务
|
// 停止 CAN 信号监测服务
|
||||||
_CANMonitoringService?.Stop();
|
_CANMonitoringService?.Stop();
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ namespace UIShare.GlobalVariable
|
|||||||
public class GlobalInfo:BindableBase
|
public class GlobalInfo:BindableBase
|
||||||
{
|
{
|
||||||
public event EventHandler? ScopeChanged;
|
public event EventHandler? ScopeChanged;
|
||||||
public Dictionary<string,ScopedContext> ContextDic { get; set; }
|
public ConcurrentDictionary<string,ScopedContext> ContextDic { get; set; }
|
||||||
public Dictionary<string,StepRunning> StepRunningDic { get; set; }
|
public ConcurrentDictionary<string,StepRunning> StepRunningDic { get; set; }
|
||||||
public Dictionary<string, SystemConfig> ConfigDic { get; set; }
|
public ConcurrentDictionary<string, SystemConfig> ConfigDic { get; set; }
|
||||||
public Dictionary<string, IScopedProvider> ScopeDic { get; set; }
|
public ConcurrentDictionary<string, IScopedProvider> ScopeDic { get; set; }
|
||||||
|
|
||||||
/// <summary>硬件指纹 → 设备实例的并发池,确保同一物理硬件全局只创建一个驱动实例。</summary>
|
/// <summary>硬件指纹 → 设备实例的并发池,确保同一物理硬件全局只创建一个驱动实例。</summary>
|
||||||
public ConcurrentDictionary<string, Lazy<IBaseInterface>> HardwarePool { get; set; }
|
public ConcurrentDictionary<string, Lazy<IBaseInterface>> HardwarePool { get; set; }
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Common.Attributes;
|
using Common.Attributes;
|
||||||
using DeviceCommand.Base;
|
using DeviceCommand.Base;
|
||||||
|
using Logger;
|
||||||
using Model.Models;
|
using Model.Models;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
@@ -41,6 +43,18 @@ namespace UIShare.GlobalVariable
|
|||||||
private CancellationTokenSource? _cts;
|
private CancellationTokenSource? _cts;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
|
/// <summary>是否已完成过至少一次 Discover(幂等标记,避免多作用域重复调用时 Clear + 重扫)</summary>
|
||||||
|
private bool _discovered;
|
||||||
|
|
||||||
|
/// <summary>连续失败次数达到此阈值后,暂停该通道采样并发布报警</summary>
|
||||||
|
private const int FailureThreshold = 5;
|
||||||
|
|
||||||
|
/// <summary>每通道连续失败计数(key = fingerprint + "|" + methodName)</summary>
|
||||||
|
private readonly ConcurrentDictionary<string, int> _failureCounts = new();
|
||||||
|
|
||||||
|
/// <summary>已因连续失败而被暂停的通道(key 同上)</summary>
|
||||||
|
private readonly ConcurrentDictionary<string, bool> _suspendedChannels = new();
|
||||||
|
|
||||||
/// <summary>采样间隔(默认 1000ms)</summary>
|
/// <summary>采样间隔(默认 1000ms)</summary>
|
||||||
public TimeSpan SampleInterval
|
public TimeSpan SampleInterval
|
||||||
{
|
{
|
||||||
@@ -89,9 +103,12 @@ namespace UIShare.GlobalVariable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 扫描 GlobalInfo.HardwarePool 中所有已创建的物理设备,
|
/// 扫描 GlobalInfo.HardwarePool 中所有已创建的物理设备,
|
||||||
/// 反查可监测方法并编译为委托。每个指纹只注册一次,反射只执行一次。
|
/// 反查可监测方法并编译为委托。每个指纹只注册一次,反射只执行一次。
|
||||||
|
/// <para>幂等:首次调用后再次调用不会 Clear 重扫,避免多作用域重复 Discover 导致短暂采样中断。</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Discover()
|
public void Discover()
|
||||||
{
|
{
|
||||||
|
if (_discovered) return;
|
||||||
|
_discovered = true;
|
||||||
_registeredMethods.Clear();
|
_registeredMethods.Clear();
|
||||||
|
|
||||||
foreach (var poolEntry in _globalInfo.HardwarePool)
|
foreach (var poolEntry in _globalInfo.HardwarePool)
|
||||||
@@ -157,6 +174,11 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
foreach (var entry in _registeredMethods)
|
foreach (var entry in _registeredMethods)
|
||||||
{
|
{
|
||||||
|
string channelKey = entry.Fingerprint + "|" + entry.MethodName;
|
||||||
|
|
||||||
|
// 已暂停的通道跳过采样
|
||||||
|
if (_suspendedChannels.ContainsKey(channelKey)) continue;
|
||||||
|
|
||||||
// fire-and-forget:每个通道独立采样,完成后自行广播
|
// fire-and-forget:每个通道独立采样,完成后自行广播
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@@ -167,6 +189,9 @@ namespace UIShare.GlobalVariable
|
|||||||
|
|
||||||
if (!double.TryParse(raw, out double value)) return;
|
if (!double.TryParse(raw, out double value)) return;
|
||||||
|
|
||||||
|
// 采样成功,重置失败计数
|
||||||
|
_failureCounts.TryRemove(channelKey, out _);
|
||||||
|
|
||||||
// 向所有引用该物理设备的作用域分别广播
|
// 向所有引用该物理设备的作用域分别广播
|
||||||
var scopes = GetScopesForFingerprint(entry.Fingerprint);
|
var scopes = GetScopesForFingerprint(entry.Fingerprint);
|
||||||
foreach (var scope in scopes)
|
foreach (var scope in scopes)
|
||||||
@@ -188,9 +213,24 @@ namespace UIShare.GlobalVariable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// 单个通道故障不干扰其他通道
|
int count = _failureCounts.AddOrUpdate(channelKey, 1, (_, c) => c + 1);
|
||||||
|
LoggerHelper.Warn($"采样通道 [{entry.Fingerprint}/{entry.MethodName}] 第 {count} 次失败: {ex.Message}");
|
||||||
|
|
||||||
|
if (count >= FailureThreshold)
|
||||||
|
{
|
||||||
|
_suspendedChannels.TryAdd(channelKey, true);
|
||||||
|
LoggerHelper.Error($"采样通道 [{entry.Fingerprint}/{entry.MethodName}] 连续失败 {count} 次,已暂停采样");
|
||||||
|
|
||||||
|
// 向所有引用该设备的作用域发布报警
|
||||||
|
var scopes = GetScopesForFingerprint(entry.Fingerprint);
|
||||||
|
foreach (var scope in scopes)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<AlarmEvent>().Publish(
|
||||||
|
(scope, entry.Fingerprint, $"通道 {entry.MethodName} 连续失败 {count} 次,已暂停"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, token);
|
}, token);
|
||||||
}
|
}
|
||||||
@@ -209,6 +249,8 @@ namespace UIShare.GlobalVariable
|
|||||||
_cts?.Dispose();
|
_cts?.Dispose();
|
||||||
_cts = null;
|
_cts = null;
|
||||||
_registeredMethods.Clear();
|
_registeredMethods.Clear();
|
||||||
|
_failureCounts.Clear();
|
||||||
|
_suspendedChannels.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UIShare.GlobalVariable;
|
using UIShare.GlobalVariable;
|
||||||
using static UIShare.UIViewModel.ParameterVM;
|
using static UIShare.UIViewModel.ParameterVM;
|
||||||
@@ -30,6 +31,7 @@ namespace UIShare
|
|||||||
private IContainerProvider containerProvider;
|
private IContainerProvider containerProvider;
|
||||||
private IEventAggregator _eventAggregator;
|
private IEventAggregator _eventAggregator;
|
||||||
private ITestReportService _testReportService;
|
private ITestReportService _testReportService;
|
||||||
|
private ITestCheckRecordService _testCheckRecordService;
|
||||||
|
|
||||||
private readonly Dictionary<Guid, ParameterVM> tmpParameters = [];
|
private readonly Dictionary<Guid, ParameterVM> tmpParameters = [];
|
||||||
|
|
||||||
@@ -39,6 +41,9 @@ namespace UIShare
|
|||||||
|
|
||||||
private readonly Stack<LoopContext> loopStack = new();
|
private readonly Stack<LoopContext> loopStack = new();
|
||||||
|
|
||||||
|
/// <summary>测试项上下文栈:进入 IsTestItem 子程序时压栈,栈非空期间每次 OKExpression 判断都归属栈顶测试项</summary>
|
||||||
|
private readonly Stack<TestItemContext> testItemStack = new();
|
||||||
|
|
||||||
public CancellationTokenSource stepCTS = new();
|
public CancellationTokenSource stepCTS = new();
|
||||||
public CancellationTokenSource errorStepCTS = new();
|
public CancellationTokenSource errorStepCTS = new();
|
||||||
private bool SubSingleStep = false;
|
private bool SubSingleStep = false;
|
||||||
@@ -47,13 +52,14 @@ namespace UIShare
|
|||||||
private volatile bool _disposed = false;
|
private volatile bool _disposed = false;
|
||||||
|
|
||||||
public Guid TestRoundID;
|
public Guid TestRoundID;
|
||||||
public StepRunning(ScopedContext ScopedContext, SystemConfig systemConfig,IEventAggregator eventAggregator, DeviceManager deviceManager, ITestReportService testReportService)
|
public StepRunning(ScopedContext ScopedContext, SystemConfig systemConfig,IEventAggregator eventAggregator, DeviceManager deviceManager, ITestReportService testReportService, ITestCheckRecordService testCheckRecordService)
|
||||||
{
|
{
|
||||||
_scopedContext = ScopedContext;
|
_scopedContext = ScopedContext;
|
||||||
_systemConfig = systemConfig;
|
_systemConfig = systemConfig;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_deviceManager= deviceManager;
|
_deviceManager= deviceManager;
|
||||||
_testReportService = testReportService;
|
_testReportService = testReportService;
|
||||||
|
_testCheckRecordService = testCheckRecordService;
|
||||||
//_devices = containerProvider.Resolve<Devices>();
|
//_devices = containerProvider.Resolve<Devices>();
|
||||||
}
|
}
|
||||||
public async Task<bool> ExecuteErrorSteps(ProgramVM program, int depth = 0, CancellationToken cancellationToken = default)
|
public async Task<bool> ExecuteErrorSteps(ProgramVM program, int depth = 0, CancellationToken cancellationToken = default)
|
||||||
@@ -259,6 +265,7 @@ namespace UIShare
|
|||||||
loopStopwatchStack.Clear();
|
loopStopwatchStack.Clear();
|
||||||
ResetAllStepStatus(program.StepCollection);
|
ResetAllStepStatus(program.StepCollection);
|
||||||
tmpParameters.Clear();
|
tmpParameters.Clear();
|
||||||
|
testItemStack.Clear();
|
||||||
TestRoundID = Guid.NewGuid();
|
TestRoundID = Guid.NewGuid();
|
||||||
}
|
}
|
||||||
int initialLoopStackCount = loopStack.Count;
|
int initialLoopStackCount = loopStack.Count;
|
||||||
@@ -387,14 +394,24 @@ namespace UIShare
|
|||||||
SubProgram = step.SubProgram,
|
SubProgram = step.SubProgram,
|
||||||
StepName = step.Name
|
StepName = step.Name
|
||||||
});
|
});
|
||||||
|
bool isTestItemStep = step.IsTestItem;
|
||||||
|
if (isTestItemStep)
|
||||||
|
{
|
||||||
|
testItemStack.Push(new TestItemContext { Name = step.Name ?? "未命名测试项" });
|
||||||
|
}
|
||||||
stepSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken);
|
stepSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken);
|
||||||
|
// 先评估本步骤自身(含自身 OKExpression 判断,归属本测试项),再写测试项汇总并弹栈
|
||||||
|
UpdateCurrentStepResult(step, true, stepSuccess, depth);
|
||||||
|
if (isTestItemStep)
|
||||||
|
{
|
||||||
|
await FinalizeTestItemAsync(depth);
|
||||||
|
}
|
||||||
// 发布退出子程序导航事件
|
// 发布退出子程序导航事件
|
||||||
_eventAggregator.GetEvent<SubProgramNavigateEvent>().Publish(new SubProgramNavigatePayload
|
_eventAggregator.GetEvent<SubProgramNavigateEvent>().Publish(new SubProgramNavigatePayload
|
||||||
{
|
{
|
||||||
Scope = _systemConfig.Title,
|
Scope = _systemConfig.Title,
|
||||||
Action = NavigateAction.Exit
|
Action = NavigateAction.Exit
|
||||||
});
|
});
|
||||||
UpdateCurrentStepResult(step, true, stepSuccess, depth);
|
|
||||||
if (SubSingleStep)
|
if (SubSingleStep)
|
||||||
{
|
{
|
||||||
SubSingleStep = false;
|
SubSingleStep = false;
|
||||||
@@ -815,12 +832,46 @@ namespace UIShare
|
|||||||
paraDic.TryAdd(item.Name, item.Value!);
|
paraDic.TryAdd(item.Name, item.Value!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool re = ExpressionEvaluator.EvaluateExpression(step.OKExpression, paraDic);
|
bool re;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
re = ExpressionEvaluator.EvaluateExpression(step.OKExpression, paraDic);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 表达式执行错误也视为 NG,并记录到测试项判断明细
|
||||||
|
LoggerHelper.ErrorWithNotify(_systemConfig.Title, $"指令 [ {step.Index} ] OKExpression 执行异常: {ex.Message}", depth: depth);
|
||||||
|
re = false;
|
||||||
|
}
|
||||||
step.Result = re ? 1 : 2;
|
step.Result = re ? 1 : 2;
|
||||||
if (step.Result == 2)
|
if (step.Result == 2)
|
||||||
{
|
{
|
||||||
LoggerHelper.WarnWithNotify(_systemConfig.Title, $"指令 [ {step.Index} ] NG:条件表达式验证失败", depth: depth);
|
LoggerHelper.WarnWithNotify(_systemConfig.Title, $"指令 [ {step.Index} ] NG:条件表达式验证失败", depth: depth);
|
||||||
}
|
}
|
||||||
|
// 测试项范围内:记录本次判断(重复判断逐次记录)
|
||||||
|
if (testItemStack.Count > 0)
|
||||||
|
{
|
||||||
|
var ctx = testItemStack.Peek();
|
||||||
|
bool isSelfCheck = ctx.Name == (step.Name ?? "未命名测试项") && step.SubProgram != null;
|
||||||
|
if (!re) ctx.HasFailure = true;
|
||||||
|
if (!isSelfCheck)
|
||||||
|
{
|
||||||
|
SaveCheckRecordAsync(new TestCheckRecordEntity
|
||||||
|
{
|
||||||
|
TestRoundId = TestRoundID,
|
||||||
|
Scope = _systemConfig.Title,
|
||||||
|
FileName = _systemConfig.CurrentACPFile ?? "",
|
||||||
|
TestItemName = ctx.Name,
|
||||||
|
StepName = step.Name ?? "",
|
||||||
|
Depth = depth,
|
||||||
|
OKExpression = step.OKExpression,
|
||||||
|
Pass = re,
|
||||||
|
Values = ExtractExpressionValues(step.OKExpression, paraDic),
|
||||||
|
IsSummary = false,
|
||||||
|
CreateTime = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -831,6 +882,90 @@ namespace UIShare
|
|||||||
}
|
}
|
||||||
step.Result = 2;
|
step.Result = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 测试项范围内的步骤执行错误/表达式 NG 均计入当前测试项汇总(自身步骤的错误已在压栈期间计入)
|
||||||
|
if (step.Result == 2 && testItemStack.Count > 0)
|
||||||
|
{
|
||||||
|
testItemStack.Peek().HasFailure = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试项执行结束:写入汇总记录(PASS/NG)并弹栈。
|
||||||
|
/// 测试项内无任何判断时,结果由范围内步骤执行成败决定。
|
||||||
|
/// </summary>
|
||||||
|
private async Task FinalizeTestItemAsync(int depth)
|
||||||
|
{
|
||||||
|
if (testItemStack.Count == 0) return;
|
||||||
|
var ctx = testItemStack.Pop();
|
||||||
|
SaveCheckRecordAsync(new TestCheckRecordEntity
|
||||||
|
{
|
||||||
|
TestRoundId = TestRoundID,
|
||||||
|
Scope = _systemConfig.Title,
|
||||||
|
FileName = _systemConfig.CurrentACPFile ?? "",
|
||||||
|
TestItemName = ctx.Name,
|
||||||
|
StepName = ctx.Name,
|
||||||
|
Depth = depth,
|
||||||
|
OKExpression = null,
|
||||||
|
Pass = !ctx.HasFailure,
|
||||||
|
Values = null,
|
||||||
|
IsSummary = true,
|
||||||
|
CreateTime = DateTime.Now
|
||||||
|
});
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存测试项判断记录(不阻塞执行主流程)
|
||||||
|
/// </summary>
|
||||||
|
private void SaveCheckRecordAsync(TestCheckRecordEntity entity)
|
||||||
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _testCheckRecordService.InsertAsync(entity);
|
||||||
|
if (!result.IsSuccess)
|
||||||
|
{
|
||||||
|
LoggerHelper.Error($"保存测试项判断记录失败 [{entity.TestItemName}]: {result.Msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.Error($"保存测试项判断记录失败 [{entity.TestItemName}]: {ex.Message}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从表达式中提取实际出现的变量并取其当前值,拼接为 "变量=值; " 格式(长名优先,避免子串误匹配)
|
||||||
|
/// </summary>
|
||||||
|
private static string? ExtractExpressionValues(string expression, Dictionary<string, object> paraDic)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var matchedSpans = new List<(int Start, int End)>();
|
||||||
|
foreach (var name in paraDic.Keys.OrderByDescending(k => k.Length))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name)) continue;
|
||||||
|
foreach (Match m in Regex.Matches(expression, $@"\b{Regex.Escape(name)}\b"))
|
||||||
|
{
|
||||||
|
bool overlaps = matchedSpans.Any(s => m.Index < s.End && m.Index + m.Length > s.Start);
|
||||||
|
if (!overlaps)
|
||||||
|
{
|
||||||
|
matchedSpans.Add((m.Index, m.Index + m.Length));
|
||||||
|
sb.Append($"{name}={paraDic[name]}; ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.Length > 0 ? sb.ToString() : null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -864,6 +999,7 @@ namespace UIShare
|
|||||||
tmpParameters.Clear();
|
tmpParameters.Clear();
|
||||||
loopStack.Clear();
|
loopStack.Clear();
|
||||||
loopStopwatchStack.Clear();
|
loopStopwatchStack.Clear();
|
||||||
|
testItemStack.Clear();
|
||||||
stepStopwatch.Stop();
|
stepStopwatch.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -879,6 +1015,13 @@ namespace UIShare
|
|||||||
public StepVM? LoopStartStep { get; set; }
|
public StepVM? LoopStartStep { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>测试项执行上下文:记录测试项名称与范围内是否出现过失败</summary>
|
||||||
|
private class TestItemContext
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public bool HasFailure { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,22 @@ namespace UIShare.PubEvent
|
|||||||
public class OverlayEvent : PubSubEvent<bool>
|
public class OverlayEvent : PubSubEvent<bool>
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作用域感知的灰度遮罩事件:仅台架名称与 Scope 匹配的台架视图显示/隐藏加载遮罩,
|
||||||
|
/// 避免全局 OverlayEvent 导致所有台架及主窗口同时变灰。
|
||||||
|
/// </summary>
|
||||||
|
public class ScopeOverlayEvent : PubSubEvent<ScopeOverlayArgs>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>作用域遮罩事件参数。</summary>
|
||||||
|
public class ScopeOverlayArgs
|
||||||
|
{
|
||||||
|
/// <summary>台架名称(与 SystemConfig.Title / TestStatus 一致)。</summary>
|
||||||
|
public string Scope { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>true 显示遮罩,false 隐藏遮罩。</summary>
|
||||||
|
public bool Show { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||