Compare commits

...

15 Commits

Author SHA1 Message Date
“hsc”
66bb6d36ba 驱动更新 2026-08-04 15:10:37 +08:00
“hsc”
7250080a24 Merge branch 'master' of https://git.kaiyili-lab.com/huangsucan/ACP 2026-08-04 11:37:31 +08:00
“hsc”
537c4554ae 设备命令修改 2026-08-04 11:37:26 +08:00
“hsc”
857bd3fb56 设备命令修改 2026-08-04 10:38:13 +08:00
ef52530a14 Merge branch 'master' of https://git.kaiyili-lab.com/huangsucan/ACP 2026-08-03 14:39:57 +08:00
7e6c342908 优化台架启动界面 2026-08-03 14:39:49 +08:00
“hsc”
df520f91ac Merge branch 'master' of https://git.kaiyili-lab.com/huangsucan/ACP 2026-08-03 14:19:30 +08:00
“hsc”
9f8cf5f267 IO板卡映射修改 2026-08-03 14:19:24 +08:00
a15334f02d 优化启动界面 2026-08-03 14:13:43 +08:00
“hsc”
21cc593d3f Merge branch 'master' of https://git.kaiyili-lab.com/huangsucan/ACP 2026-08-03 13:44:01 +08:00
“hsc”
63af886f26 串口连接修复 2026-08-03 13:07:48 +08:00
“hsc”
2e74627b31 设备类优化 2026-08-03 11:05:26 +08:00
“hsc”
5cfb8af9e4 添加环境箱驱动 2026-08-03 09:18:08 +08:00
“hsc”
ceae6e3421 chroma61800设备添加 2026-07-31 17:28:47 +08:00
“hsc”
e90dc805fc 回读图像功能优化 2026-07-31 09:03:09 +08:00
35 changed files with 1126 additions and 2547 deletions

View File

@@ -9,7 +9,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="ncalc" Version="1.3.8" /> <PackageReference Include="ncalc" Version="1.3.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.5-beta1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.5-beta1" />
<PackageReference Include="System.IO.Ports" Version="11.0.0-preview.4.26230.115" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,6 @@
using CurveModule.Views; using CurveModule.Views;
using CurveModule.Views.Dialogs;
using System.Reflection; using System.Reflection;
namespace CurveModule namespace CurveModule
@@ -14,6 +15,7 @@ namespace CurveModule
public void RegisterTypes(IContainerRegistry containerRegistry) public void RegisterTypes(IContainerRegistry containerRegistry)
{ {
containerRegistry.RegisterForNavigation<CurveRecallView>("CurveRecallView"); containerRegistry.RegisterForNavigation<CurveRecallView>("CurveRecallView");
containerRegistry.RegisterForNavigation<CurveStatisticsView>("CurveStatistics");
} }
} }

View File

@@ -0,0 +1,22 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 水冷机 一对一
/// </summary>
[ACPCommand]
public class ANEVH80 : Tcp
{
public ANEVH80(TcpConfig config) : base(config)
{
}
}
}

View File

@@ -0,0 +1,207 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 交流源 一拖三
/// </summary>
[ACPCommand]
public class Chroma61800 : Tcp
{
// Chroma SCPI 默认使用 \n 作为结束符
private const string ScpiDelimiter = "\n";
public Chroma61800(TcpConfig config) : base(config)
{
}
#region 1.
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*CLS{ScpiDelimiter}", ct);
}
#endregion
#region 2. LIST
/// <summary>
/// 初始化 LIST 模式的三相独立编辑环境
/// </summary>
/// <param name="loopCount">循环次数0代表无限循环</param>
public virtual async Task List模式(int loopCount = 0, CancellationToken ct = default)
{
await SendAsync($"INST:PHAS THRE{ScpiDelimiter}", ct); // 选择三相模式
await SendAsync($"INST:EDIT EACH{ScpiDelimiter}", ct); // 选择编辑电压方式为分别编辑
await SendAsync($"LIST:COUN {loopCount}{ScpiDelimiter}", ct); // 循环次数设定
await SendAsync($"LIST:TRIG AUTO{ScpiDelimiter}", ct); // 设定触发方式
await SendAsync($"LIST:BASE TIME{ScpiDelimiter}", ct); // 选择执行时间类别
}
#endregion
#region 3. LIST ( - )
/// <summary>
/// 切换目标相位并设置波形为正弦波
/// 建议在发送具体 LIST 参数前调用一次即可
/// </summary>
/// <param name="phase">相位 (1: L1, 2: L2, 3: L3)</param>
public virtual async Task Async(int phase, CancellationToken ct = default)
{
if (phase < 1 || phase > 3)
throw new ArgumentOutOfRangeException(nameof(phase), "相位必须为 1, 2, 或 3");
await SendAsync($"INST:NSEL {phase}{ScpiDelimiter}", ct);
await SendAsync($"FUNC:SHAP:A SINE{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步起始角度
/// </summary>
public virtual async Task List起始角度Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:DEGR {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步交流起始电压
/// </summary>
public virtual async Task List交流起始电压Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:VOLT:AC:STAR {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步交流结束电压
/// </summary>
public virtual async Task List交流结束电压Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:VOLT:AC:END {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步直流起始电压
/// </summary>
public virtual async Task List直流起始电压Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:VOLT:DC:STAR {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步直流结束电压
/// </summary>
public virtual async Task List直流结束电压Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:VOLT:DC:END {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步起始频率
/// </summary>
public virtual async Task List起始频率Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:FREQ:STAR {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步结束频率
/// </summary>
public virtual async Task List结束频率Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:FREQ:END {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 配置 LIST 各步执行时间 (单位: 毫秒)
/// </summary>
public virtual async Task List执行时间Async(double step1, double step2, double step3, CancellationToken ct = default)
{
string strValues = JoinParameters(step1, step2, step3);
await SendAsync($"LIST:DWEL {strValues}{ScpiDelimiter}", ct);
}
/// <summary>
/// 辅助方法:将三个 double 值转换为逗号分隔的字符串,确保小数点格式正确
/// </summary>
private string JoinParameters(double val1, double val2, double val3)
{
return $"{val1.ToString("0.###", CultureInfo.InvariantCulture)}," +
$"{val2.ToString("0.###", CultureInfo.InvariantCulture)}," +
$"{val3.ToString("0.###", CultureInfo.InvariantCulture)}";
}
#endregion
#region 4. LIST
/// <summary>
/// 切换到 LIST 模式并触发输出
/// </summary>
public virtual async Task List输出(CancellationToken ct = default)
{
await SendAsync($"OUTP:MODE LIST{ScpiDelimiter}", ct);
await SendAsync($"TRIG ON{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询 LIST 序列是否正在运行
/// </summary>
public virtual async Task<bool> List是否运行中(CancellationToken ct = default)
{
string state = await WriteReadAsync($"TRIG:STATE?{ScpiDelimiter}", ScpiDelimiter, ct);
state = state?.Trim().ToUpper();
return state == "ON" || state == "1";
}
/// <summary>
/// 阻塞异步等待直到 LIST 序列执行完毕 (带安全超时保护)
/// </summary>
/// <param name="timeoutMs">最大等待超时时间建议比实际LIST总时长多5-10秒</param>
public virtual async Task List执行结束(int timeoutMs = 60000, CancellationToken ct = default)
{
// 给仪器留出响应触发的时间
await Task.Delay(500, ct);
Stopwatch sw = Stopwatch.StartNew();
bool isRunning = true;
while (isRunning)
{
ct.ThrowIfCancellationRequested();
if (sw.ElapsedMilliseconds > timeoutMs)
{
throw new TimeoutException($"等待 LIST 执行结束超时 ({timeoutMs} ms)。");
}
// 读取当前状态
isRunning = await List是否运行中(ct);
if (isRunning)
{
// 还在运行,适当休眠后继续轮询,避免总线阻塞
await Task.Delay(500, ct);
}
}
}
#endregion
}
}

View File

@@ -8,6 +8,9 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary>
/// 信号源 一拖三
/// </summary>
#region #region
/// <summary> /// <summary>

View File

@@ -11,6 +11,9 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary>
/// IO板卡 一对一由于板卡映射不完全一样采用IOGroup
/// </summary>
//[ACPCommand] //[ACPCommand]
public class IOBoard : ModbusTcp public class IOBoard : ModbusTcp
{ {

View File

@@ -11,14 +11,26 @@ using System.ComponentModel;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary> /// <summary>
/// 系统控制功能类型枚举 (中文) /// 系统控制功能类型枚举 (根据三工位 ACP接线图 更新)
/// </summary> /// </summary>
public enum : byte public enum : byte
{ {
, ,
, ,
, KL30通断,
KL15通断,
60,
1K欧电阻,
CC信号串1K欧电阻,
CP信号串1K欧电阻,
CC信号串680欧电阻,
CC信号串220欧电阻,
CC信号串100欧电阻,
CC信号空载,
,
, // (接线图中标注于 Y14, KM8)
HV, // (接线图中标注于 Y15, KM9)
LV // (接线图中标注于 Y16, KM10)
} }
/// <summary> /// <summary>
@@ -26,7 +38,7 @@ namespace DeviceCommand.Devices
/// </summary> /// </summary>
public struct IoPortInfo public struct IoPortInfo
{ {
public int ModuleIndex { get; set; } // IO板卡/模块序号 (1 或 2) public int ModuleIndex { get; set; } // IO板卡/模块序号 (1、23)
public ushort Address { get; set; } // 转换为 Modbus 的线圈相对偏移地址 (Y1->0, Y2->1) public ushort Address { get; set; } // 转换为 Modbus 的线圈相对偏移地址 (Y1->0, Y2->1)
public IoPortInfo(int moduleIndex, ushort address) public IoPortInfo(int moduleIndex, ushort address)
@@ -39,79 +51,89 @@ namespace DeviceCommand.Devices
[ACPCommand] [ACPCommand]
public class IOBoardGroup public class IOBoardGroup
{ {
// 声明底层的 2 个 IO 板卡实例 // 声明底层的 3 个 IO 板卡实例(对应三工位系统)
public IOBoard Board1 { get; private set; } public IOBoard Board1 { get; private set; }
public IOBoard Board2 { get; private set; } public IOBoard Board2 { get; private set; }
public IOBoard Board3 { get; private set; }
private readonly byte _slaveAddress = 1; // 默认 Modbus 从站号为 1 private readonly byte _slaveAddress = 1; // 默认 Modbus 从站号为 1
// 核心:严格对齐图片数据的双层嵌套配置字典 (2个模块1-8台架) // 核心:根据 ACP硬件接线图 严格对齐的数据字典 (3个模块独立对应1-3工位)
private static readonly Dictionary<int, Dictionary<, IoPortInfo>> IoMapping private static readonly Dictionary<int, Dictionary<, IoPortInfo>> IoMapping
= new Dictionary<int, Dictionary<, IoPortInfo>>() = new Dictionary<int, Dictionary<, IoPortInfo>>()
{ {
// 1# 工位 -> IO继电器模块 1#
{ 1, new Dictionary<, IoPortInfo> { { 1, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(1, 5) }, // Y6 -> 5 { ., new IoPortInfo(1, 0) }, // Y1 -> 0 (KM2)
{ ., new IoPortInfo(1, 0) }, // Y1 -> 0 { ., new IoPortInfo(1, 1) }, // Y2 -> 1 (KM3)
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1 { .KL30通断, new IoPortInfo(1, 2) }, // Y3 -> 2
{ ., new IoPortInfo(2, 15) } // Y9 -> 8 { .KL15通断, new IoPortInfo(1, 3) }, // Y4 -> 3
{ .60, new IoPortInfo(1, 4) }, // Y5 -> 4
{ .1K欧电阻, new IoPortInfo(1, 5) }, // Y6 -> 5
{ .CC信号串1K欧电阻, new IoPortInfo(1, 6) }, // Y7 -> 6
{ .CP信号串1K欧电阻, new IoPortInfo(1, 7) }, // Y8 -> 7
{ .CC信号串680欧电阻, new IoPortInfo(1, 8) }, // Y9 -> 8
{ .CC信号串220欧电阻, new IoPortInfo(1, 9) }, // Y10 -> 9
{ .CC信号串100欧电阻, new IoPortInfo(1, 10) }, // Y11 -> 10
{ .CC信号空载, new IoPortInfo(1, 11) }, // Y12 -> 11
{ ., new IoPortInfo(1, 12) }, // Y13 -> 12
{ ., new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
{ .HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
{ .LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
}}, }},
// 2# 工位 -> IO继电器模块 2#
{ 2, new Dictionary<, IoPortInfo> { { 2, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(1, 6) }, // Y7 -> 6 { ., new IoPortInfo(2, 0) }, // Y1 -> 0 (KM4)
{ ., new IoPortInfo(1, 2) }, // Y3 -> 2 { ., new IoPortInfo(2, 1) }, // Y2 -> 1 (KM5)
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1 { .KL30通断, new IoPortInfo(2, 2) }, // Y3 -> 2
{ ., new IoPortInfo(2, 14) } // Y10 -> 9 { .KL15通断, new IoPortInfo(2, 3) }, // Y4 -> 3
{ .60, new IoPortInfo(2, 4) }, // Y5 -> 4
{ .1K欧电阻, new IoPortInfo(2, 5) }, // Y6 -> 5
{ .CC信号串1K欧电阻, new IoPortInfo(2, 6) }, // Y7 -> 6
{ .CP信号串1K欧电阻, new IoPortInfo(2, 7) }, // Y8 -> 7
{ .CC信号串680欧电阻, new IoPortInfo(2, 8) }, // Y9 -> 8
{ .CC信号串220欧电阻, new IoPortInfo(2, 9) }, // Y10 -> 9
{ .CC信号串100欧电阻, new IoPortInfo(2, 10) }, // Y11 -> 10
{ .CC信号空载, new IoPortInfo(2, 11) }, // Y12 -> 11
{ ., new IoPortInfo(2, 12) }, // Y13 -> 12
{ ., new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
{ .HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
{ .LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
}}, }},
// 3# 工位 -> IO继电器模块 3# (包含额外的抛负载及短路测试控制)
{ 3, new Dictionary<, IoPortInfo> { { 3, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(1, 7) }, // Y8 -> 7 { ., new IoPortInfo(3, 0) }, // Y1 -> 0 (KM6)
{ ., new IoPortInfo(1, 3) }, // Y4 -> 3 { ., new IoPortInfo(3, 1) }, // Y2 -> 1 (KM7)
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1 { .KL30通断, new IoPortInfo(3, 2) }, // Y3 -> 2
{ ., new IoPortInfo(2, 13) } // Y11 -> 10 { .KL15通断, new IoPortInfo(3, 3) }, // Y4 -> 3
}}, { .60, new IoPortInfo(3, 4) }, // Y5 -> 4
{ 4, new Dictionary<, IoPortInfo> { { .1K欧电阻, new IoPortInfo(3, 5) }, // Y6 -> 5
{ ., new IoPortInfo(1, 8) }, // Y9 -> 8 { .CC信号串1K欧电阻, new IoPortInfo(3, 6) }, // Y7 -> 6
{ ., new IoPortInfo(1, 4) }, // Y5 -> 4 { .CP信号串1K欧电阻, new IoPortInfo(3, 7) }, // Y8 -> 7
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1 { .CC信号串680欧电阻, new IoPortInfo(3, 8) }, // Y9 -> 8
{ ., new IoPortInfo(2, 12) } // Y12 -> 11 { .CC信号串220欧电阻, new IoPortInfo(3, 9) }, // Y10 -> 9
}}, { .CC信号串100欧电阻, new IoPortInfo(3, 10) }, // Y11 -> 10
{ 5, new Dictionary<, IoPortInfo> { { .CC信号空载, new IoPortInfo(3, 11) }, // Y12 -> 11
{ ., new IoPortInfo(2, 4) }, // Y5 -> 4 { ., new IoPortInfo(3, 12) }, // Y13 -> 12
{ ., new IoPortInfo(2, 0) }, // Y1 -> 0 { ., new IoPortInfo(3, 13) }, // Y14 -> 13 (KM8)
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1 { .HV, new IoPortInfo(3, 14) }, // Y15 -> 14 (KM9)
{ ., new IoPortInfo(2, 11) } // Y13 -> 12 { .LV, new IoPortInfo(3, 15) } // Y16 -> 15 (KM10)
}},
{ 6, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(2, 5) }, // Y6 -> 5
{ ., new IoPortInfo(2, 1) }, // Y2 -> 1
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1
{ ., new IoPortInfo(2, 10) } // Y14 -> 13
}},
{ 7, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(2, 6) }, // Y7 -> 6
{ ., new IoPortInfo(2, 2) }, // Y3 -> 2
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1
{ ., new IoPortInfo(2, 9) } // Y15 -> 14
}},
{ 8, new Dictionary<, IoPortInfo> {
{ ., new IoPortInfo(2, 7) }, // Y8 -> 7
{ ., new IoPortInfo(2, 3) }, // Y4 -> 3
{ ., new IoPortInfo(1, 1) }, // Y2 -> 1
{ ., new IoPortInfo(2, 8) } // Y16 -> 15
}} }}
}; };
public IOBoardGroup(IOBoard board1, IOBoard board2) // 修改构造函数以支持 3 个板块
public IOBoardGroup(IOBoard board1, IOBoard board2, IOBoard board3)
{ {
Board1 = board1 ?? throw new ArgumentNullException(nameof(board1), "IOBoard Board1 实例不能为空。"); Board1 = board1 ?? throw new ArgumentNullException(nameof(board1), "IOBoard Board1 实例不能为空。");
Board2 = board2 ?? throw new ArgumentNullException(nameof(board2), "IOBoard Board2 实例不能为空。"); Board2 = board2 ?? throw new ArgumentNullException(nameof(board2), "IOBoard Board2 实例不能为空。");
Board3 = board3 ?? throw new ArgumentNullException(nameof(board3), "IOBoard Board3 实例不能为空。");
} }
/// <summary> /// <summary>
/// 执行指定台架的功能动作控制(带高低电平开关及底层安全互锁) /// 执行指定台架的功能动作控制(带高低电平开关及底层安全互锁)
/// </summary> /// </summary>
/// <param name="台架序号">台架/工位序号 (1 - 8)</param> /// <param name="台架序号">台架/工位序号 (1 - 3)</param>
/// <param name="动作">控制功能类型 (单相充电, 抛负载, 短路测试, 蜂鸣器报警)</param> /// <param name="动作">控制功能类型</param>
/// <param name="开关状态">开关状态 (true: 开启吸合, false: 关闭断开)</param> /// <param name="开关状态">开关状态 (true: 开启吸合, false: 关闭断开)</param>
/// <param name="取消令牌">异步取消令牌</param> /// <param name="取消令牌">异步取消令牌</param>
public async Task Async(int , , bool , CancellationToken = default) public async Task Async(int , , bool , CancellationToken = default)
@@ -119,23 +141,27 @@ namespace DeviceCommand.Devices
// 1. 根据传入的台架序号和动作检索字典映射关系 // 1. 根据传入的台架序号和动作检索字典映射关系
if (!IoMapping.TryGetValue(, out var ) || !.TryGetValue(, out var io信息)) if (!IoMapping.TryGetValue(, out var ) || !.TryGetValue(, out var io信息))
{ {
throw new ArgumentException($"未找到台架 [{台架序号}] 对应功能 [{动作}] 的 IO 点位映射配置。"); throw new ArgumentException($"未找到台架 [{台架序号}] 对应功能 [{动作}] 的 IO 点位映射配置。请注意抛负载及短路测试仅存在于 3# 工位。");
} }
// 2. 软件防错高危互锁逻辑 // 2. 软件防错高危互锁逻辑:防止同时执行充电和放电
if () if ()
{ {
if ( == .) if ( == .)
{ {
var = IoMapping[][.]; if (IoMapping[].TryGetValue(., out var ))
await GetBoardInstance(.ModuleIndex).(_slaveAddress, .Address, false); {
await GetBoardInstance(.ModuleIndex).(_slaveAddress, .Address, false);
} }
else if ( == .) }
else if ( == .)
{
if (IoMapping[].TryGetValue(., out var ))
{ {
var = IoMapping[][.];
await GetBoardInstance(.ModuleIndex).(_slaveAddress, .Address, false); await GetBoardInstance(.ModuleIndex).(_slaveAddress, .Address, false);
} }
} }
}
// 3. 动态获取目标板卡实例并直接驱动开关端口 // 3. 动态获取目标板卡实例并直接驱动开关端口
IOBoard = GetBoardInstance(io信息.ModuleIndex); IOBoard = GetBoardInstance(io信息.ModuleIndex);
@@ -151,7 +177,8 @@ namespace DeviceCommand.Devices
{ {
1 => Board1, 1 => Board1,
2 => Board2, 2 => Board2,
_ => throw new ArgumentOutOfRangeException(nameof(moduleIndex), "非法的板卡序号,系统仅支持 1# 和 2# 模块。") 3 => Board3,
_ => throw new ArgumentOutOfRangeException(nameof(moduleIndex), "非法的板卡序号,系统仅支持 1#、2# 和 3# 模块。")
}; };
return board ?? throw new InvalidOperationException($"IO板卡模块 [{moduleIndex}#] 尚未完成初始化,无法建立总线通信。"); return board ?? throw new InvalidOperationException($"IO板卡模块 [{moduleIndex}#] 尚未完成初始化,无法建立总线通信。");

View File

@@ -1,300 +0,0 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
[ACPCommand]
public class IT6015C : Tcp
{
// SCPI 指令结束符
private const string ScpiDelimiter = "\n";
public IT6015C(TcpConfig config) : base(config)
{
}
#region 1. IEEE 488.2
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*CLS{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*RST{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*STB?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> SCPI版本号(CancellationToken ct = default)
{
return await WriteReadAsync($":SYSTem:VERSion?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 2.
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:REMote{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:LOCal{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:RWLock{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"SYSTem:ERRor?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
#endregion
#region 3.
/// <summary>
/// 设置电源优先工作模式 (CV 电压优先 或 CC 电流优先)
/// </summary>
public virtual async Task (IT6000CPriorityMode , CancellationToken ct = default)
{
await SendAsync($"FUNCTION {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FUNCTION?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置瞬变/功能模式 (固定, LIST, 电池测试, PV曲线, 汽车波形)
/// </summary>
public virtual async Task (IT6000CFunctionMode , CancellationToken ct = default)
{
await SendAsync($"FUNCTION:MODE {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FUNCTION:MODE?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 4.
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"OUTPut {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"OUTPut:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"OUTPut:PROTection:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
#endregion
#region 5. /
/// <summary>
/// 设置输出电压 (仅在 CV 模式下生效)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置输出电流 (仅在 CC 模式下生效)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置 CV 优先模式下的电流限制上限值 (I+, 限流保护)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent:LIMit:POSitive {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"CURRent:LIMit:POSitive?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置电压上升/下降斜率 (单位: V/s, 数值越大变化越快)[cite: 1]
/// </summary>
public virtual async Task (double Vs, CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage:SLEW {0:F4}{1}", Vs, ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
#region 6. (OVP, OCP, OPP)
/// <summary>
/// 开启或关闭过压保护 (OVP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"VOLTage:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage:PROTection {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
/// <summary>
/// 开启或关闭过流保护 (OCP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"CURRent:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent:PROTection {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
/// <summary>
/// 开启或关闭过功率保护 (OPP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"POWER:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "POWER:PROTection {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
#region 7. (MEASure FETCh)
[Monitorable("直流源载实际电压")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("直流源载实际电流")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("直流源载实际功率")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:POWER?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:VOLTage:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:CURRent:MINimum?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 8. /CR模式设置 ( CC )
/// <summary>
/// 开启或关闭恒阻 (CR) 负载模式[cite: 1]
/// </summary>
/// <remarks>执行该指令前,必须确保设备处于 CC 优先模式,否则会报错</remarks>
public virtual async Task CR模式开关(bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"SINK:RESistance:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> CR模式开关(CancellationToken ct = default)
{
return await WriteReadAsync($"SINK:RESistance:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置恒阻 (CR) 模式下的电阻值 (单位: Ω)[cite: 1]
/// </summary>
/// <remarks>必须在先开启 CR 模式才能使用,设置为 0 相当于关闭 CR 模式</remarks>
public virtual async Task CR电阻值(double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "SINK:RESistance {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> CR电阻值(CancellationToken ct = default)
{
return await WriteReadAsync($"SINK:RESistance?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
}
}

View File

@@ -1,331 +0,0 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
#region
/// <summary>
/// IT6000C 优先工作模式 (CV优先 / CC优先)
/// </summary>
public enum IT6000CPriorityMode
{
/// <summary> 恒压优先模式 (CV优先) </summary>
VOLTage,
/// <summary> 恒流优先模式 (CC优先) </summary>
CURRent
}
/// <summary>
/// IT6000C 瞬变功能模式
/// </summary>
public enum IT6000CFunctionMode
{
/// <summary> 固定模式 (普通输出) </summary>
FIXed,
/// <summary> List 列表模式 </summary>
LIST,
/// <summary> 电池测试模式 </summary>
BATTer,
/// <summary> 光伏/太阳能板曲线模拟模式 </summary>
SOLar,
/// <summary> 汽车波形模拟模式 </summary>
CARProfile
}
#endregion
[ACPCommand]
public class IT6036C : Tcp
{
// SCPI 指令结束符
private const string ScpiDelimiter = "\n";
public IT6036C (TcpConfig config) : base(config)
{
}
#region 1. IEEE 488.2
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*CLS{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*RST{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*STB?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> SCPI版本号(CancellationToken ct = default)
{
return await WriteReadAsync($":SYSTem:VERSion?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 2.
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:REMote{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:LOCal{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:RWLock{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"SYSTem:ERRor?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
#endregion
#region 3.
/// <summary>
/// 设置电源优先工作模式 (CV 电压优先 或 CC 电流优先)
/// </summary>
public virtual async Task (IT6000CPriorityMode , CancellationToken ct = default)
{
await SendAsync($"FUNCTION {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FUNCTION?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置瞬变/功能模式 (固定, LIST, 电池测试, PV曲线, 汽车波形)
/// </summary>
public virtual async Task (IT6000CFunctionMode , CancellationToken ct = default)
{
await SendAsync($"FUNCTION:MODE {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FUNCTION:MODE?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 4.
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"OUTPut {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"OUTPut:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"OUTPut:PROTection:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
#endregion
#region 5. /
/// <summary>
/// 设置输出电压 (仅在 CV 模式下生效)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置输出电流 (仅在 CC 模式下生效)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置 CV 优先模式下的电流限制上限值 (I+, 限流保护)
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent:LIMit:POSitive {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"CURRent:LIMit:POSitive?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置电压上升/下降斜率 (单位: V/s, 数值越大变化越快)[cite: 1]
/// </summary>
public virtual async Task (double Vs, CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage:SLEW {0:F4}{1}", Vs, ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
#region 6. (OVP, OCP, OPP)
/// <summary>
/// 开启或关闭过压保护 (OVP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"VOLTage:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "VOLTage:PROTection {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
/// <summary>
/// 开启或关闭过流保护 (OCP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"CURRent:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "CURRent:PROTection {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
/// <summary>
/// 开启或关闭过功率保护 (OPP)[cite: 1]
/// </summary>
public virtual async Task (bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"POWER:PROTection:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "POWER:PROTection {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
#region 7. (MEASure FETCh)
[Monitorable("直流源载实际电压")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("直流源载实际电流")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("直流源载实际功率")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"MEASure:POWER?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:VOLTage:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:CURRent:MINimum?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 8. /CR模式设置 ( CC )
/// <summary>
/// 开启或关闭恒阻 (CR) 负载模式[cite: 1]
/// </summary>
/// <remarks>执行该指令前,必须确保设备处于 CC 优先模式,否则会报错</remarks>
public virtual async Task CR模式开关(bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF";
await SendAsync($"SINK:RESistance:STATe {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> CR模式开关(CancellationToken ct = default)
{
return await WriteReadAsync($"SINK:RESistance:STATe?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 设置恒阻 (CR) 模式下的电阻值 (单位: Ω)[cite: 1]
/// </summary>
/// <remarks>必须在先开启 CR 模式才能使用,设置为 0 相当于关闭 CR 模式</remarks>
public virtual async Task CR电阻值(double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, "SINK:RESistance {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> CR电阻值(CancellationToken ct = default)
{
return await WriteReadAsync($"SINK:RESistance?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
}
}

View File

@@ -9,17 +9,19 @@ using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary>
/// 低压电源 一拖三
/// </summary>
[ACPCommand] [ACPCommand]
public class IT6720 : Serial_Port // 直接继承您提供的 Serial_Port 基类 public class IT6720: Serial_Port // 直接继承您提供的 Serial_Port 基类
{ {
// 帧格式:同步头 AA + 地址 + 命令字 + 数据[4-25] + 校验和,共 26 字节 // 帧格式:同步头 AA + 地址 + 命令字 + 数据[4-25] + 校验和,共 26 字节
private const byte FrameSyncByte = 0xAA; private const byte FrameSyncByte = 0xAA;
private const int FrameLength = 26; private const int FrameLength = 26;
private readonly byte _deviceAddress; // 电源地址 private readonly byte _deviceAddress = 0x00; // 电源地址
public IT6720(SerialPortConfig config, byte address = 0x00) : base(config) public IT6720(SerialPortConfig config) : base(config)
{ {
_deviceAddress = address;
} }
#region #region

View File

@@ -1,263 +0,0 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
#region
/// <summary>
/// 电源电气/耦合模式
/// </summary>
public enum PowerCouplingMode
{
/// <summary> 纯交流模式 </summary>
AC,
/// <summary> 纯直流模式 </summary>
DC,
/// <summary> 交直流混合模式 </summary>
ACDC,
/// <summary> 直交流混合模式 </summary>
DCAC
}
/// <summary>
/// 设备操作运行角色/模式
/// </summary>
public enum DeviceOperationMode
{
/// <summary> 电压源模式(常规源) </summary>
VOLTage,
/// <summary> 电子负载模式(放电拉载) </summary>
LOAD,
/// <summary> 电流源模式 </summary>
CURRent
}
#endregion
[ACPCommand]
public class IT7800E : Tcp
{
// 八台产品共用一个,使用换行符 \n (ASCII 0x0A) 作为 SCPI 结束符[cite: 1]
private const string ScpiDelimiter = "\n";
public IT7800E(TcpConfig config) : base(config)
{
}
#region 1. IEEE 488.2
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*CLS{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"*RST{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*STB?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 2.
/// <summary>
/// 设置电源输出源的电气模式/工作耦合模式 (AC, DC, ACDC, DCAC)[cite: 1]
/// </summary>
public virtual async Task (PowerCouplingMode , CancellationToken ct = default)
{
await SendAsync($":SOURce:FUNCtion {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
/// <summary>
/// 查询电源当前的工作电气模式[cite: 1]
/// </summary>
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":SOURce:FUNCtion?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task DC输出(bool , CancellationToken ct = default)
{
string = ? "ON" : "OFF"; //[cite: 1]
await SendAsync($":OUTPut {参数}{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task<string> DC输出状态(CancellationToken ct = default)
{
return await WriteReadAsync($":OUTPut?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 3. (AC //DC )
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:VOLTage {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:VOLTage:DC {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:FREQuency {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:CURRent {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
#region 4.
[Monitorable("交流电源电压")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("交流电源电流")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
[Monitorable("交流电源功率")]
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:POWer?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:POWer:APParent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:FREQuency?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":MEASure:POWer:PFACtor?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
#endregion
#region 5.
public virtual async Task _OCP(double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:CURRent:PROTection:RMS {0:F3}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task _OVP(double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:VOLTage:PROTection:PEAK {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($":SYSTem:ERRor?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($":SYSTem:REMote{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($":SYSTem:LOCal{ScpiDelimiter}", ct); //[cite: 1]
}
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($":OUTPut:PROTection:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
#endregion
#region 6.
/// <summary>
/// 清除最近的安时Ah与瓦时Wh统计数据[cite: 1]
/// </summary>
public virtual async Task (CancellationToken ct = default)
{
await SendAsync($"SENSe:AHOur:RESet{ScpiDelimiter}", ct); //[cite: 1]
await SendAsync($"SENSe:WHOur:RESet{ScpiDelimiter}", ct); //[cite: 1]
}
/// <summary>
/// 读取当前充放电累计的安时值 (单位: Ah)[cite: 1]
/// </summary>
public virtual async Task<string> _Ah(CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:AHOur?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 读取当前充放电累计的瓦时值 (单位: Wh)[cite: 1]
/// </summary>
public virtual async Task<string> _Wh(CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:WHOur?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 读取充放电积分持续时间 (单位: 秒)[cite: 1]
/// </summary>
public virtual async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"FETCh:ENERgy:TIME?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
/// <summary>
/// 切换设备的操作模式 (VOLTage: 电源模式, LOAD: 负载模式/放电, CURRent: 电流源)[cite: 1]
/// </summary>
public virtual async Task (DeviceOperationMode , CancellationToken ct = default)
{
await SendAsync($"SYSTem:OPERation:MODE {模式}{ScpiDelimiter}", ct); //[cite: 1]
}
/// <summary>
/// 设置放电截止电压/电压下限 (单位: V用于防止电池过放)[cite: 1]
/// </summary>
public virtual async Task (double , CancellationToken ct = default)
{
string cmd = string.Format(CultureInfo.InvariantCulture, ":SOURce:VOLTage:LIMit:LOW {0:F2}{1}", , ScpiDelimiter); //[cite: 1]
await SendAsync(cmd, ct);
}
#endregion
}
}

View File

@@ -0,0 +1,20 @@
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 水冷机 一拖三
/// </summary>
public class MCc30W:ModbusTcp
{
public MCc30W(TcpConfig config) : base(config)
{
}
}
}

View File

@@ -1,13 +1,14 @@
using Common; using Common;
using Common.Attributes; using Common.Attributes;
using DeviceCommand.Base; using DeviceCommand.Base;
using Model.Models;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DeviceCommand.Device namespace DeviceCommand.Device
{ {
/// <summary> /// <summary>
/// 功率分析仪型号HIOKI PW8001 /// 功率分析仪型号HIOKI PW8001 一拖三
/// </summary> /// </summary>
public class PW8001 : Tcp public class PW8001 : Tcp
@@ -15,9 +16,8 @@ namespace DeviceCommand.Device
/// <summary> /// <summary>
/// 构造函数SCPI 通信使用端口 23 /// 构造函数SCPI 通信使用端口 23
/// </summary> /// </summary>
public PW8001() public PW8001(TcpConfig config) : base(config)
{ {
Port = 23;
} }
/// <summary> /// <summary>

View File

@@ -0,0 +1,206 @@
using Common.Attributes;
using DeviceCommand.Base;
using Model.Models;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace DeviceCommand.Devices
{
/// <summary>
/// 环境箱型号RLT1000 一拖三
/// </summary>
[ACPCommand]
public class RLT1000 : ModbusTcp
{
public RLT1000(TcpConfig config) : base(config)
{
}
private const byte SlaveId = 1;
#region
public async Task<ushort> (CancellationToken ct = default)
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 5, 1, ct);
return re[0];
}
public async Task<float> (CancellationToken ct = default)
{
try
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 10, 2, ct);
return ConvertToFloat(re);
}
catch (Exception)
{
return 0;
}
}
public async Task<float> 湿(CancellationToken ct = default)
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 16, 2, ct);
return ConvertToFloat(re);
}
public async Task<float> (CancellationToken ct = default)
{
try
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 22, 2, ct);
return ConvertToFloat(re);
}
catch (Exception)
{
return 0;
}
}
public async Task<float> 1(CancellationToken ct = default)
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 28, 2, ct);
return ConvertToFloat(re);
}
public async Task<float> 2(CancellationToken ct = default)
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 34, 2, ct);
return ConvertToFloat(re);
}
public async Task<float> 3(CancellationToken ct = default)
{
ushort[] re = await ReadHoldingRegistersAsync(SlaveId, 40, 2, ct);
return ConvertToFloat(re);
}
#endregion
#region
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 147, 0, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 147, 1, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 148, 0, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 148, 1, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 149, 0, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 149, 1, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 153, 0, ct);
}
public async Task (CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 153, 1, ct);
}
public async Task (float , CancellationToken ct = default)
{
var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 154, tmp, ct);
}
public async Task 湿(float 湿, CancellationToken ct = default)
{
var tmp = ConvertFromFloat(湿);
await WriteMultipleRegistersAsync(SlaveId, 158, tmp, ct);
}
public async Task (float , CancellationToken ct = default)
{
var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 162, tmp, ct);
}
public async Task 1(float , CancellationToken ct = default)
{
var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 164, tmp, ct);
}
public async Task 2(float , CancellationToken ct = default)
{
var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 166, tmp, ct);
}
public async Task 3(float , CancellationToken ct = default)
{
var tmp = ConvertFromFloat();
await WriteMultipleRegistersAsync(SlaveId, 168, tmp, ct);
}
public async Task 使(CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 1181, 0, ct);
}
public async Task 使(CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 1181, 1, ct);
}
public async Task 使(CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 1182, 0, ct);
}
public async Task 使(CancellationToken ct = default)
{
await WriteSingleRegisterAsync(SlaveId, 1182, 1, ct);
}
#endregion
#region
private float ConvertToFloat(ushort[] values)
{
if (values == null || values.Length < 2) return 0f;
byte[] bytes = new byte[4];
bytes[3] = (byte)(values[0] >> 8);
bytes[2] = (byte)(values[0] & 0xFF);
bytes[1] = (byte)(values[1] >> 8);
bytes[0] = (byte)(values[1] & 0xFF);
return BitConverter.ToSingle(bytes, 0);
}
private ushort[] ConvertFromFloat(float value)
{
byte[] bytes = BitConverter.GetBytes(value);
ushort[] result = new ushort[2];
result[0] = (ushort)((bytes[3] << 8) | bytes[2]);
result[1] = (ushort)((bytes[1] << 8) | bytes[0]);
return result;
}
#endregion
}
}

View File

@@ -0,0 +1,213 @@
using Common.Attributes;
using DeviceCommand.Base;
using System;
using System.Threading;
using System.Threading.Tasks;
// 根据您的项目实际路径保留枚举的引用
// using 通讯驱动Base.枚举;
namespace DeviceCommand.Devices
{
public enum S7200负载模式_枚举
{
CC,
CV,
CP,
CR,
CVCC,
CRCC,
CVCR,
AUTO
}
public enum S7200直流源模式_枚举
{
VOLTage,
CURRent,
BATTest,
BATSim,
SOLar,
EN50530,
LIST,
UDW,
SQUare,
TRIangle,
SINusoid,
DIN40839_12V,
DIN40839_24V,
IS016750_SHORTDROP_12V,
IS016750_SHORTDROP_24V,
IS016750_RESETTEST,
IS016750_STARTINGPROFILE_12V_1,
IS016750_STARTINGPROFILE_12V_2,
IS016750_STARTINGPROFILE_12V_3,
IS016750_STARTINGPROFILE_12V_4,
IS016750_STARTINGPROFILE_24V_1,
IS016750_STARTINGPROFILE_24V_2,
IS016750_STARTINGPROFILE_24V_3,
IS016750_LOADDUMP_TESTA_12V,
IS016750_LOADDUMP_TESTA_24V,
IS016750_LOADDUMP_TESTB_12V,
IS016750_LOADDUMP_TESTB_24V,
LV123_HV_1,
LV123_HV_2A,
LV123_HV_2B,
LV123_HV_3
}
public enum S7200工作模式_枚举
{
,
,
}
/// <summary>
/// 高压直流源载一体机 (适配 IT6000C SCPI指令集)
/// </summary>
[ACPCommand]
public class S7200 : Tcp
{
// SCPI 指令结束符
private const string ScpiDelimiter = "\n";
#region
public async Task (CancellationToken ct = default)
{
await SendAsync($"SYSTem:REMote{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (S7200工作模式_枚举 , CancellationToken ct = default)
{
// IT6000C 为双向无缝切换,底层硬件无需显式发送源载切换指令
// 保留此方法仅为兼容您原有上位机的工步调用流程,直接返回即可
await Task.CompletedTask;
}
public async Task (S7200直流源模式_枚举 , CancellationToken ct = default)
{
// 原指令: MODE {模式} -> 现 IT6000C 指令: FUNCtion {模式} (如 VOLTage / CURRent)[cite: 1]
// 若枚举 ToString() 后不匹配,建议在此处加 Switch 转换
await SendAsync($"FUNCtion {模式}{ScpiDelimiter}", ct);
}
public async Task (S7200负载模式_枚举 , CancellationToken ct = default)
{
// IT6000C 负载控制通过负向边界实现。若是想开启纯电阻模式,可调用 SINK:RESistance:STATe ON[cite: 1]
await Task.CompletedTask;
}
public async Task (bool , CancellationToken ct = default)
{
await SendAsync($"OUTPut {(开关 ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task CC模式电流(double , CancellationToken ct = default)
{
await SendAsync($"CURRent {电流}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task CC电压H(double , CancellationToken ct = default) // 沿用原参数名“电流”,实际是设定电压上限
{
await SendAsync($"VOLTage:LIMit:POSitive {电流}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task CC电压L(double , CancellationToken ct = default) // 沿用原参数名“电流”,实际是设定电压下限
{
await SendAsync($"VOLTage:LIMit:NEGative {电流}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task CV正向电流(double , CancellationToken ct = default)
{
await SendAsync($"CURRent:LIMit:POSitive {电流}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task CV反向电流(double , CancellationToken ct = default)
{
// 提醒: 此处传入的参数如果是正数,建议在外部或此处转换为负值,因为载模式限制需要负数[cite: 1]
await SendAsync($"CURRent:LIMit:NEGative {电流}{ScpiDelimiter}", ct);
}
public async Task (double , CancellationToken ct = default)
{
await SendAsync($"POWer:LIMit:POSitive {功率}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (double , CancellationToken ct = default)
{
// 提醒: 此处传入的参数必须为负数[cite: 1]
await SendAsync($"POWer:LIMit:NEGative {功率}{ScpiDelimiter}", ct);
}
public async Task (double , CancellationToken ct = default)
{
await SendAsync($"VOLTage {电压}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (CancellationToken ct = default)
{
await SendAsync($"OUTPut:PROTection:CLEar{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task OCP开关(bool v, CancellationToken ct = default)
{
await SendAsync($"CURRent:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task OVP开关(bool v, CancellationToken ct = default)
{
await SendAsync($"VOLTage:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task OVP电压(double oVP电压, CancellationToken ct = default)
{
await SendAsync($"VOLTage:PROTection {oVP电压}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task OCP电流(double oCP电流, CancellationToken ct = default)
{
await SendAsync($"CURRent:PROTection {oCP电流}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (bool v, CancellationToken ct = default)
{
await SendAsync($"POWer:PROTection:STATe {(v ? 1 : 0)}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (double oPP功率, CancellationToken ct = default)
{
await SendAsync($"POWer:PROTection {oPP功率}{ScpiDelimiter}", ct); //[cite: 1]
}
public async Task (string , CancellationToken ct = default)
{
await SendAsync($"{指令}{ScpiDelimiter}", ct);
}
#endregion
#region
public async Task<string> (CancellationToken ct = default)
{
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
}
public async Task<double> (CancellationToken ct = default)
{
var str = await WriteReadAsync($"MEASure:VOLTage?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
return double.TryParse(str, out double result) ? result : 0;
}
public async Task<double> (CancellationToken ct = default)
{
var str = await WriteReadAsync($"MEASure:CURRent?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
return double.TryParse(str, out double result) ? result : 0;
}
public async Task<double> (CancellationToken ct = default)
{
var str = await WriteReadAsync($"MEASure:POWer?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1]
return double.TryParse(str, out double result) ? result : 0;
}
#endregion
}
}

View File

@@ -3,11 +3,16 @@ using DeviceCommand.Base;
using Model.Models; using Model.Models;
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace DeviceCommand.Devices namespace DeviceCommand.Devices
{ {
/// <summary>
/// 示波器 MSO 4/5/6 系列
/// </summary>
#region #region
/// <summary> /// <summary>
@@ -45,7 +50,7 @@ namespace DeviceCommand.Devices
[ACPCommand] [ACPCommand]
public class TektronixMSO : Tcp public class TektronixMSO : Tcp
{ {
// SCPI 指令结束符 (文档中提到消息终止符需使用 LF)[cite: 1] // SCPI 指令结束符 (消息终止符需使用 LF)
private const string ScpiDelimiter = "\n"; private const string ScpiDelimiter = "\n";
public TektronixMSO(TcpConfig config) : base(config) public TektronixMSO(TcpConfig config) : base(config)
@@ -56,19 +61,19 @@ namespace DeviceCommand.Devices
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 返回仪器的标识代码[cite: 1] // 返回仪器的标识代码[cite: 2]
return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"*IDN?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 查询仪器是否处于忙碌状态,常用于同步操作[cite: 1] // 查询仪器是否处于忙碌状态,常用于同步操作[cite: 2]
return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"BUSY?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
public virtual async Task (CancellationToken ct = default) public virtual async Task (CancellationToken ct = default)
{ {
// 指示仪器执行信号路径校准 (SPC),需要几分钟时间[cite: 1] // 指示仪器执行信号路径校准 (SPC)[cite: 2]
await SendAsync($"*CAL?{ScpiDelimiter}", ct); await SendAsync($"*CAL?{ScpiDelimiter}", ct);
} }
@@ -81,19 +86,20 @@ namespace DeviceCommand.Devices
/// </summary> /// </summary>
public virtual async Task (bool , CancellationToken ct = default) public virtual async Task (bool , CancellationToken ct = default)
{ {
// 启动、停止或返回采集状态[cite: 1] // 启动、停止或返回采集状态[cite: 2]
string = ? "RUN" : "STOP"; string = ? "RUN" : "STOP";
await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct); await SendAsync($"ACQuire:STATE {参数}{ScpiDelimiter}", ct);
} }
public virtual async Task<string> (CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct); //[cite: 1] // 启动、停止或返回采集状态[cite: 2]
return await WriteReadAsync($"ACQuire:STATE?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
public virtual async Task (TekAcquisitionMode , CancellationToken ct = default) public virtual async Task (TekAcquisitionMode , CancellationToken ct = default)
{ {
// 设置或查询采集模式[cite: 1] // 设置或查询采集模式[cite: 2]
await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct); await SendAsync($"ACQuire:MODe {模式}{ScpiDelimiter}", ct);
} }
@@ -103,34 +109,33 @@ namespace DeviceCommand.Devices
public virtual async Task (double , CancellationToken ct = default) public virtual async Task (double , CancellationToken ct = default)
{ {
// 设置或查询水平刻度 (Scale)[cite: 1] // 设置或查询水平刻度 (Scale)[cite: 2]
string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", , ScpiDelimiter); string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SCAle {0:E}{1}", , ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
public virtual async Task (long , CancellationToken ct = default) public virtual async Task (long , CancellationToken ct = default)
{ {
// 设置或查询记录长度[cite: 1] // 设置或查询水平记录长度[cite: 2]
await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct); await SendAsync($"HORizontal:RECOrdlength {长度}{ScpiDelimiter}", ct);
} }
public virtual async Task (double , CancellationToken ct = default) public virtual async Task<string> (CancellationToken ct = default)
{ {
// 设置或查询水平采样率[cite: 1] // 查询水平采样率[cite: 2]
string cmd = string.Format(CultureInfo.InvariantCulture, "HORizontal:SAMPLERate {0:E}{1}", , ScpiDelimiter); return await WriteReadAsync($"HORizontal:SAMPLERate?{ScpiDelimiter}", ScpiDelimiter, ct);
await SendAsync(cmd, ct);
} }
#endregion #endregion
#region 4. (Display & Vertical) #region 4. (Vertical)
/// <summary> /// <summary>
/// 全局启用或关闭某通道的显示 /// 全局启用或关闭某通道的显示
/// </summary> /// </summary>
public virtual async Task (int , bool , CancellationToken ct = default) public virtual async Task (int , bool , CancellationToken ct = default)
{ {
// 设置或查询指定通道显示模式(开或关)[cite: 1] // 4/5/6 Series MSO 标准通道显示指令[cite: 2]
string = ? "ON" : "OFF"; string = ? "ON" : "OFF";
await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct); await SendAsync($"DISplay:GLObal:CH{通道号}:STATE {参数}{ScpiDelimiter}", ct);
} }
@@ -140,8 +145,7 @@ namespace DeviceCommand.Devices
/// </summary> /// </summary>
public virtual async Task (int , double , CancellationToken ct = default) public virtual async Task (int , double , CancellationToken ct = default)
{ {
// 设置或查询指定Waveform View内指定通道的垂直缩放比例WaveView<x> 通常为1[cite: 1] string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:SCAle {1:E}{2}", , , ScpiDelimiter);
string cmd = string.Format(CultureInfo.InvariantCulture, "DISplay:WAVEView1:CH{0}:VERTical:SCAle {1:E}{2}", , , ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
@@ -150,8 +154,7 @@ namespace DeviceCommand.Devices
/// </summary> /// </summary>
public virtual async Task (int , double , CancellationToken ct = default) public virtual async Task (int , double , CancellationToken ct = default)
{ {
// 设置或查询指定Waveform View内指定通道的垂直位置以格为单位[cite: 1] string cmd = string.Format(CultureInfo.InvariantCulture, "CH{0}:POSition {1:F2}{2}", , , ScpiDelimiter);
string cmd = string.Format(CultureInfo.InvariantCulture, "DISplay:WAVEView1:CH{0}:VERTical:POSition {1:F2}{2}", , , ScpiDelimiter);
await SendAsync(cmd, ct); await SendAsync(cmd, ct);
} }
@@ -160,37 +163,185 @@ namespace DeviceCommand.Devices
#region 5. (Measurement) #region 5. (Measurement)
/// <summary> /// <summary>
/// 新增一个测量项槽位 /// 动态添加并开启一个测量项
/// </summary> /// </summary>
public virtual async Task (CancellationToken ct = default) public virtual async Task (int , CancellationToken ct = default)
{ {
// 添加一个测量项[cite: 1] // 4/5/6 Series MSO 必须先动态创建测量项[cite: 2]
await SendAsync($"MEASUrement:ADDMEAS{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:ADDNew \"MEAS{}\"{ScpiDelimiter}", ct);
} }
public virtual async Task (int , TekMeasurementType , CancellationToken ct = default) public virtual async Task (int , TekMeasurementType , CancellationToken ct = default)
{ {
// 设置或查询测量类型[cite: 1] // 设置或查询测量类型[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:MEAS{测量项编号}:TYPe {类型}{ScpiDelimiter}", ct);
} }
public virtual async Task (int , string , CancellationToken ct = default) public virtual async Task (int , string , CancellationToken ct = default)
{ {
// 设置或查询局部输入源 (例如 CH1, CH2)[cite: 1] // 4/5/6 Series 中设置测量源[cite: 2]
await SendAsync($"MEASUrement:MEAS{测量项编号}:SOURCE {源名称}{ScpiDelimiter}", ct); await SendAsync($"MEASUrement:MEAS{测量项编号}:SOUrce1 {源名称}{ScpiDelimiter}", ct);
} }
[Monitorable("测量项当前采集平均值")] [Monitorable("测量项当前采集平均值")]
public virtual async Task<string> (int , CancellationToken ct = default) public virtual async Task<string> (int , CancellationToken ct = default)
{ {
// 返回当前采集的指定测量的平均值[cite: 1] // 4/5/6 Series 获取当前采集值的标准方式,查询平均值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
public virtual async Task<string> (int , CancellationToken ct = default) public virtual async Task<string> (int , CancellationToken ct = default)
{ {
// 返回自上次统计重置以来指定测量的最大值(当前采集)[cite: 1] // 获取所有采集(AllAcqs)历史累积的平均值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:CURRentacq:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct); return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MEAN?{ScpiDelimiter}", ScpiDelimiter, ct);
}
public virtual async Task<string> (int , CancellationToken ct = default)
{
// 获取所有采集(AllAcqs)历史累积的最大值[cite: 2]
return await WriteReadAsync($"MEASUrement:MEAS{测量项编号}:RESUlts:ALLAcqs:MAXimum?{ScpiDelimiter}", ScpiDelimiter, ct);
}
#endregion
#region 6. (Hard Copy & File System)
/// <summary>
/// 捕获示波器屏幕并保存到上位机本地路径
/// </summary>
/// <param name="上位机文件路径">保存到电脑本地的文件路径 (例如: @"C:\temp\screen.png")</param>
/// <param name="ct">异步取消令牌</param>
public virtual async Task (string , CancellationToken ct = default)
{
string = "E:/temp1.png";
// 1. 直接保存截图到示波器U盘 (自动识别 .png 扩展名)[cite: 2]
await SendAsync($"SAVe:IMAGe \"{}\"{ScpiDelimiter}", ct);
// 2. 发送 *OPC? 并等待返回 1确保图像文件已经完全写入磁盘[cite: 2]
await WriteReadAsync($"*OPC?{ScpiDelimiter}", ScpiDelimiter, ct);
// 3. 将文件内容读取到当前通信接口[cite: 2]
await SendAsync($"FILESystem:READFile \"{}\"{ScpiDelimiter}", ct);
// 4. 提取底层的二进制块 (Block Data)
byte[] imageBytes = await ReadBinaryBlockAsync(ct);
// 5. 将提取的二进制字节流写入上位机本地磁盘
await File.WriteAllBytesAsync(, imageBytes, ct);
// 6. 删除示波器上的临时文件,保持设备存储干净[cite: 2]
await SendAsync($"FILESystem:DELEte \"{}\"{ScpiDelimiter}", ct);
}
/// <summary>
/// ⚠️ 请在这里将您的网络流对接起来!
/// 依据您 Tcp 基类的设计,返回用于读取底层字节流的 Stream。
/// </summary>
protected virtual Stream GetNetworkStream()
{
// 【修改提示】:
// 如果您的基类暴露了 TcpClient请取消注释并使用这一行
// return this.TcpClient.GetStream();
// 如果您的基类直接叫 _stream 或 Stream请返回它
// return this.Stream;
// 假如不知道怎么填,请查看 Tcp 类的源码。
throw new NotImplementedException("请在此方法中返回基类的 NetworkStream 或 Socket 流对象。");
}
/// <summary>
/// 辅助方法:读取 IEEE 488.2 标准的二进制块数据 (Block Data)
/// </summary>
protected virtual async Task<byte[]> ReadBinaryBlockAsync(CancellationToken ct = default)
{
var stream = GetNetworkStream();
byte[] singleByte = new byte[1];
// 1. 逐字节读取,直到读到起始符 '#'
while (true)
{
int read = await stream.ReadAsync(singleByte, 0, 1, ct);
if (read == 0) throw new EndOfStreamException("通信中断:未找到二进制块起始符 '#'");
if (singleByte[0] == (byte)'#') break;
}
// 2. 读取 1 个字符 <NZDig>,它代表接下来有多少个字符表示数据长度
await stream.ReadAsync(singleByte, 0, 1, ct);
char nzDigChar = (char)singleByte[0];
if (!char.IsDigit(nzDigChar)) throw new InvalidDataException($"无效的块头标识: {nzDigChar}");
int lengthBytesCount = int.Parse(nzDigChar.ToString());
// 3. 读取 <NZDig> 所指示的长度字符,将其解析为整数 N
byte[] lengthBytes = new byte[lengthBytesCount];
int currentRead = 0;
while (currentRead < lengthBytesCount)
{
int read = await stream.ReadAsync(lengthBytes, currentRead, lengthBytesCount - currentRead, ct);
if (read == 0) throw new EndOfStreamException("通信中断:读取数据长度失败");
currentRead += read;
}
int dataLength = int.Parse(Encoding.ASCII.GetString(lengthBytes));
// 4. 从流中精确读取 N 个字节 <DChar>...,这就是完整的图像数据
byte[] imageData = new byte[dataLength];
int totalBytesRead = 0;
while (totalBytesRead < dataLength)
{
int read = await stream.ReadAsync(imageData, totalBytesRead, dataLength - totalBytesRead, ct);
if (read == 0) throw new EndOfStreamException("通信中断:读取图像数据不完整");
totalBytesRead += read;
}
// 5. 结尾可能包含 <EOM> (例如 LF \n),将其读取并丢弃以清空缓冲区
try
{
await stream.ReadAsync(singleByte, 0, 1, ct);
}
catch { /* 忽略末尾符读取的可能超时 */ }
return imageData;
}
#endregion
#region 7. (Status and Error)
/// <summary>
/// 清除所有的状态寄存器和错误/事件队列
/// </summary>
public virtual async Task Async(CancellationToken ct = default)
{
// 发送 *CLS 指令清空状态[cite: 2]
await SendAsync($"*CLS{ScpiDelimiter}", ct);
}
/// <summary>
/// 查询错误队列中当前的事件/错误数量
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回事件队列中的事件数量
return await WriteReadAsync($"EVQty?{ScpiDelimiter}", ScpiDelimiter, ct);
}
/// <summary>
/// 从错误队列中读取最早的一个事件/错误(包含代码和详细消息),并将其从队列中弹出
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回事件队列中的事件代码和消息
return await WriteReadAsync($"EVMsg?{ScpiDelimiter}", ScpiDelimiter, ct);
}
/// <summary>
/// 读取并清空队列中的所有错误和事件消息
/// </summary>
public virtual async Task<string> Async(CancellationToken ct = default)
{
// 返回所有事件及其消息
return await WriteReadAsync($"ALLEv?{ScpiDelimiter}", ScpiDelimiter, ct);
} }
#endregion #endregion

View File

@@ -1,147 +0,0 @@
using DeviceCommand.Devices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
public class IT6015CViewModel : NavigateViewModelBase, IDisposable
{
#region
private readonly DeviceManager _deviceManager;
private IT6015C? _device;
private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "IT6015C";
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;
/// <summary>正在执行设备命令时为 true用于 UI 忙碌状态指示。</summary>
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
private string _responseLog = string.Empty;
/// <summary>命令响应日志(最新消息在顶部)。</summary>
public string ResponseLog
{
get => _responseLog;
set => SetProperty(ref _responseLog, value);
}
#endregion
#region
#endregion
public IT6015CViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#region / Navigation
/// <summary>
/// 从 DeviceManager 中查找IT6015C设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null)
{
IT6015C? found = null;
string? foundName = null;
if (deviceName != null &&
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is IT6015C e)
{
found = e;
foundName = deviceName;
}
else
{
foreach (var kv in _deviceManager.DeviceMap)
{
if (kv.Value is IT6015C it)
{
found = it;
foundName = kv.Key;
break;
}
}
}
_device = found;
DeviceName = foundName ?? "IT7800E (未找到)";
IsConnected = _device?.IsConnected ?? false;
AppendLog(found != null
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
}
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_device == null)
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _device.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
}
}
private void AppendLog(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion
}
}

View File

@@ -1,147 +0,0 @@
using DeviceCommand.Devices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
public class IT6036CViewModel : NavigateViewModelBase, IDisposable
{
#region
private readonly DeviceManager _deviceManager;
private IT6036C? _device;
private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "IT6036C";
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;
/// <summary>正在执行设备命令时为 true用于 UI 忙碌状态指示。</summary>
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
private string _responseLog = string.Empty;
/// <summary>命令响应日志(最新消息在顶部)。</summary>
public string ResponseLog
{
get => _responseLog;
set => SetProperty(ref _responseLog, value);
}
#endregion
#region
#endregion
public IT6036CViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#region / Navigation
/// <summary>
/// 从 DeviceManager 中查找IT6036C设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null)
{
IT6036C? found = null;
string? foundName = null;
if (deviceName != null &&
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is IT6036C e)
{
found = e;
foundName = deviceName;
}
else
{
foreach (var kv in _deviceManager.DeviceMap)
{
if (kv.Value is IT6036C it)
{
found = it;
foundName = kv.Key;
break;
}
}
}
_device = found;
DeviceName = foundName ?? "IT7800E (未找到)";
IsConnected = _device?.IsConnected ?? false;
AppendLog(found != null
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
}
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_device == null)
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _device.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
}
}
private void AppendLog(string message)
{
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 4000
? line + "\n" + ResponseLog[..3000]
: line + "\n" + ResponseLog;
}
#endregion
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion
}
}

View File

@@ -1,310 +0,0 @@
using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Threading;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace DeviceEditModule.ViewModels
{
/// <summary>
/// IT7800E 交直流电源控制面板 ViewModel。
/// <para>
/// 注册为 Navigation View既可由 Region 导航进入,
/// 也可由外部直接实例化后作为 Tab 内容塞入 DialogMangerView
/// <code>
/// var view = container.Resolve&lt;IT7800EView&gt;();
/// (view.DataContext as IT7800EViewModel)?.Initialize("IT7800E");
/// _eventAggregator.GetEvent&lt;AddDialogTabEvent&gt;().Publish(
/// new DialogTabInfo { Title = "IT7800E", Content = view });
/// </code>
/// </para>
/// </summary>
public class IT7800EViewModel : NavigateViewModelBase, IDisposable
{
#region
private readonly DeviceManager _deviceManager;
private IT7800E? _device;
private CancellationTokenSource? _cts;
#endregion
#region
private string _deviceName = "IT7800E";
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;
/// <summary>正在执行设备命令时为 true用于 UI 忙碌状态指示。</summary>
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
#endregion
#region
private double _acVoltage = 220.0;
/// <summary>待设置的交流电压值V。</summary>
public double AcVoltage
{
get => _acVoltage;
set => SetProperty(ref _acVoltage, value);
}
private double _dcVoltage = 0.0;
/// <summary>待设置的直流偏置电压值V。</summary>
public double DcVoltage
{
get => _dcVoltage;
set => SetProperty(ref _dcVoltage, value);
}
private double _frequency = 50.0;
/// <summary>待设置的交流频率Hz。</summary>
public double Frequency
{
get => _frequency;
set => SetProperty(ref _frequency, value);
}
private double _currentLimit = 10.0;
/// <summary>待设置的限流值A。</summary>
public double CurrentLimit
{
get => _currentLimit;
set => SetProperty(ref _currentLimit, value);
}
private PowerCouplingMode _selectedMode = PowerCouplingMode.AC;
/// <summary>待设置的电源工作模式AC / DC / ACDC。</summary>
public PowerCouplingMode SelectedMode
{
get => _selectedMode;
set => SetProperty(ref _selectedMode, value);
}
private double _ovpValue = 260.0;
/// <summary>过压保护值V。</summary>
public double OvpValue
{
get => _ovpValue;
set => SetProperty(ref _ovpValue, value);
}
private double _ocpValue = 15.0;
/// <summary>过流保护值A。</summary>
public double OcpValue
{
get => _ocpValue;
set => SetProperty(ref _ocpValue, value);
}
#endregion
#region
private string _measuredVoltage = "—";
public string MeasuredVoltage
{
get => _measuredVoltage;
set => SetProperty(ref _measuredVoltage, value);
}
private string _measuredCurrent = "—";
public string MeasuredCurrent
{
get => _measuredCurrent;
set => SetProperty(ref _measuredCurrent, value);
}
private string _measuredPower = "—";
public string MeasuredPower
{
get => _measuredPower;
set => SetProperty(ref _measuredPower, value);
}
private string _measuredFrequency = "—";
public string MeasuredFrequency
{
get => _measuredFrequency;
set => SetProperty(ref _measuredFrequency, value);
}
private string _responseLog = string.Empty;
/// <summary>命令响应日志(最新消息在顶部)。</summary>
public string ResponseLog
{
get => _responseLog;
set => SetProperty(ref _responseLog, value);
}
#endregion
#region
public ICommand QueryIdentityCommand { get; }
public ICommand ResetDeviceCommand { get; }
public ICommand OutputOnCommand { get; }
public ICommand OutputOffCommand { get; }
public ICommand SetModeCommand { get; }
public ICommand SetAcVoltageCommand { get; }
public ICommand SetDcVoltageCommand { get; }
public ICommand SetFrequencyCommand { get; }
public ICommand SetCurrentCommand { get; }
public ICommand QueryAllMeasureCommand { get; }
public ICommand SetRemoteModeCommand { get; }
public ICommand SetLocalModeCommand { get; }
public ICommand SetOvpCommand { get; }
public ICommand SetOcpCommand { get; }
public ICommand ClearAlarmCommand { get; }
public ICommand ClearErrorCommand { get; }
#endregion
public IT7800EViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
QueryIdentityCommand = new DelegateCommand(async () => await Exec(async () => AppendLog("IDN: " + await _device!.(Ct()))));
ResetDeviceCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("设备已重置"); }));
OutputOnCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.DC输出(true, Ct()); AppendLog("输出已开启"); }));
OutputOffCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.DC输出(false, Ct()); AppendLog("输出已关闭"); }));
SetModeCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(SelectedMode, Ct()); AppendLog($"模式已设为 {SelectedMode}"); }));
SetAcVoltageCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(AcVoltage, Ct()); AppendLog($"AC电压已设为 {AcVoltage} V"); }));
SetDcVoltageCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(DcVoltage, Ct()); AppendLog($"DC偏置已设为 {DcVoltage} V"); }));
SetFrequencyCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Frequency, Ct()); AppendLog($"频率已设为 {Frequency} Hz"); }));
SetCurrentCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(CurrentLimit, Ct()); AppendLog($"限流已设为 {CurrentLimit} A"); }));
SetOvpCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._OVP(OvpValue, Ct()); AppendLog($"OVP已设为 {OvpValue} V"); }));
SetOcpCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._OCP(OcpValue, Ct()); AppendLog($"OCP已设为 {OcpValue} A"); }));
SetRemoteModeCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("已切换到远程控制模式"); }));
SetLocalModeCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("已切换到本地控制模式"); }));
ClearAlarmCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("保护告警已清除"); }));
ClearErrorCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("错误队列已清除"); }));
QueryAllMeasureCommand = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVoltage = await _device!.(Ct());
MeasuredCurrent = await _device!.(Ct());
MeasuredPower = await _device!.(Ct());
MeasuredFrequency = await _device!.(Ct());
AppendLog($"测量 → 电压:{MeasuredVoltage}V 电流:{MeasuredCurrent}A 功率:{MeasuredPower}W 频率:{MeasuredFrequency}Hz");
}));
Initialize();
}
#region / Navigation
/// <summary>
/// 从 DeviceManager 中查找 IT7800E 设备实例。
/// 优先按 <paramref name="deviceName"/> 查找,否则取第一个匹配类型的设备。
/// </summary>
public void Initialize(string? deviceName = null)
{
IT7800E? found = null;
string? foundName = null;
if (deviceName != null &&
_deviceManager.DeviceMap.TryGetValue(deviceName, out var d) &&
d is IT7800E e)
{
found = e;
foundName = deviceName;
}
else
{
foreach (var kv in _deviceManager.DeviceMap)
{
if (kv.Value is IT7800E it)
{
found = it;
foundName = kv.Key;
break;
}
}
}
_device = found;
DeviceName = foundName ?? "IT7800E (未找到)";
IsConnected = _device?.IsConnected ?? false;
AppendLog(found != null
? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "" : "")}"
: "未在 DeviceManager 中找到 IT7800E 设备,请先初始化设备配置。");
}
public override void OnNavigatedTo(NavigationContext context)
{
var name = context.Parameters.GetValue<string?>("DeviceName");
Initialize(name);
}
#endregion
#region
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
private async Task Exec(Func<Task> action)
{
if (_device == null)
{
AppendLog("错误:未关联到设备实例,请检查设备配置。");
return;
}
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _device.IsConnected;
}
catch (OperationCanceledException)
{
AppendLog("命令超时或已取消。");
}
catch (Exception ex)
{
AppendLog($"错误:{ex.Message}");
}
finally
{
IsBusy = false;
}
}
private void AppendLog(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();
}
}
}

View File

@@ -1,15 +0,0 @@
<UserControl x:Class="DeviceEditModule.Views.IT6015CView"
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">
<Grid>
</Grid>
</UserControl>

View File

@@ -1,28 +0,0 @@
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.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
{
/// <summary>
/// IT6015CView.xaml 的交互逻辑
/// </summary>
public partial class IT6015CView : UserControl
{
public IT6015CView()
{
InitializeComponent();
}
}
}

View File

@@ -1,16 +0,0 @@
<Page x:Class="DeviceEditModule.Views.IT6036CView"
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">
<Grid>
</Grid>
</Page>

View File

@@ -1,28 +0,0 @@
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.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
{
/// <summary>
/// IT6036CView.xaml 的交互逻辑
/// </summary>
public partial class IT6036CView : Page
{
public IT6036CView()
{
InitializeComponent();
}
}
}

View File

@@ -1,272 +0,0 @@
<UserControl x:Class="DeviceEditModule.Views.IT7800EView"
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="IT7800E 交直流可编程电源"
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>
<!-- ═══ 主体 2 列 ═══ -->
<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,4,4,4">
<!-- 开关输出 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="主输出" Style="{StaticResource ParamLabel}"/>
<Button Content="开启输出" Command="{Binding OutputOnCommand}"
Style="{StaticResource MaterialDesignRaisedButton}"
Background="#388E3C" Foreground="White"
Height="32" Padding="12,0" FontSize="12" Margin="4,0"/>
<Button Content="关闭输出" Command="{Binding OutputOffCommand}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
<!-- 工作模式 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="工作模式" Style="{StaticResource ParamLabel}"/>
<ComboBox Width="90" Height="32" Margin="4,0"
materialDesign:HintAssist.Hint=""
SelectedItem="{Binding SelectedMode}"
VerticalContentAlignment="Center" FontSize="12">
<ComboBoxItem Content="AC"/>
<ComboBoxItem Content="DC"/>
<ComboBoxItem Content="ACDC"/>
</ComboBox>
<Button Content="设置模式" Command="{Binding SetModeCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<!-- 远程/本地 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="控制模式" Style="{StaticResource ParamLabel}"/>
<Button Content="远程控制" Command="{Binding SetRemoteModeCommand}"
Style="{StaticResource CmdBtn}"/>
<Button Content="本地控制" Command="{Binding SetLocalModeCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<!-- 清除 / 重置 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="系统操作" Style="{StaticResource ParamLabel}"/>
<Button Content="清除告警" Command="{Binding ClearAlarmCommand}"
Style="{StaticResource WarnBtn}"/>
<Button Content="清除错误" Command="{Binding ClearErrorCommand}"
Style="{StaticResource WarnBtn}"/>
<Button Content="重置设备" Command="{Binding ResetDeviceCommand}"
Style="{StaticResource WarnBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<!-- 参数设置 -->
<GroupBox Header="参数设置" Margin="0,0,0,8"
materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4,4,4,4">
<!-- AC 电压 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="AC电压 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding AcVoltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetAcVoltageCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<!-- DC 偏置 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="DC偏置 (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding DcVoltage, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetDcVoltageCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<!-- 频率 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="频率 (Hz)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding Frequency, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetFrequencyCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<!-- 限流 -->
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="限流 (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding CurrentLimit, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置" Command="{Binding SetCurrentCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
</StackPanel>
</GroupBox>
<!-- 保护设置 -->
<GroupBox Header="保护设置" Margin="0,0,0,8"
materialDesign:ColorZoneAssist.Mode="PrimaryLight">
<StackPanel Margin="4,4,4,4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="OVP (V)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding OvpValue, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置 OVP" Command="{Binding SetOvpCommand}"
Style="{StaticResource CmdBtn}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="OCP (A)" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource NumInput}"
materialDesign:HintAssist.Hint=""
Text="{Binding OcpValue, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="设置 OCP" Command="{Binding SetOcpCommand}"
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,4,4,4">
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="实际电压" Style="{StaticResource ParamLabel}"/>
<TextBox Style="{StaticResource MeasureBox}"
materialDesign:HintAssist.Hint=""
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}"
materialDesign:HintAssist.Hint=""
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}"
materialDesign:HintAssist.Hint=""
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}"
materialDesign:HintAssist.Hint=""
Text="{Binding MeasuredFrequency, Mode=OneWay}"/>
<TextBlock Text="Hz" VerticalAlignment="Center" Margin="2,0,8,0"/>
</StackPanel>
<Button Content="刷新全部测量" Command="{Binding QueryAllMeasureCommand}"
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 QueryIdentityCommand}"
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}"
materialDesign:HintAssist.Hint=""
IsReadOnly="True"
TextWrapping="Wrap"
FontSize="11"
FontFamily="Consolas"
Background="#FAFAFA"
BorderThickness="0"
VerticalAlignment="Top"/>
</ScrollViewer>
</GroupBox>
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</UserControl>

View File

@@ -1,28 +0,0 @@
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.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
{
/// <summary>
/// IT7800EView.xaml 的交互逻辑
/// </summary>
public partial class IT7800EView : UserControl
{
public IT7800EView()
{
InitializeComponent();
}
}
}

View File

@@ -4,82 +4,86 @@
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"
prism:ViewModelLocator.AutoWireViewModel="True"> prism:ViewModelLocator.AutoWireViewModel="True">
<Grid> <Grid>
<!-- 背景:多层柔光圆 -->
<Grid.Background> <Grid.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#F8FAFC" Offset="0"/> <GradientStop Color="#E8F0FE" Offset="0"/>
<GradientStop Color="#E2E8F0" Offset="1"/> <GradientStop Color="#F3E8FF" Offset="0.5"/>
<GradientStop Color="#FCE7F3" Offset="1"/>
</LinearGradientBrush> </LinearGradientBrush>
</Grid.Background> </Grid.Background>
<materialDesign:Card Width="320" Height="380" <!-- 背景装饰光斑 -->
UniformCornerRadius="16" <Ellipse Width="200" Height="200" Fill="#93C5FD" Opacity="0.3"
Background="#FFFFFF" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="-50,-50,0,0"/>
materialDesign:ElevationAssist.Elevation="Dp4" <Ellipse Width="150" Height="150" Fill="#C4B5FD" Opacity="0.3"
HorizontalAlignment="Center" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-30,-30"/>
VerticalAlignment="Center">
<Grid Margin="30"> <!-- 中心磨砂卡片 -->
<Border Width="380" Height="440" CornerRadius="20"
Background="#CCFFFFFF" BorderBrush="#E2E8F0" BorderThickness="1"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Border.Effect>
<DropShadowEffect BlurRadius="40" ShadowDepth="0" Opacity="0.12"/>
</Border.Effect>
<Grid Margin="32">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,8,0,0"> <!-- 顶部 -->
<materialDesign:PackIcon Kind="Chip" <StackPanel Grid.Row="0" HorizontalAlignment="Center">
Width="24" Height="24" <Border Width="64" Height="64" Background="#E0E7FF" CornerRadius="32">
<materialDesign:PackIcon Kind="Chip" Width="32" Height="32"
Foreground="#4F46E5"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Foreground="#94A3B8"/> VerticalAlignment="Center"/>
<TextBlock Text="{Binding TestStatus}" </Border>
FontSize="15"
FontWeight="SemiBold" <StackPanel Orientation="Horizontal" Margin="0,10,0,0"
Foreground="#64748B" HorizontalAlignment="Center">
Margin="0,6,0,0"
HorizontalAlignment="Center"/> <TextBlock Text="{Binding TestStatus}" FontSize="13" Foreground="#64748B"/>
</StackPanel>
</StackPanel> </StackPanel>
<Grid Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"> <!-- 中央按钮 -->
<Ellipse Width="150" Height="150" Fill="#F1F5F9"/> <Grid Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Width="130" Height="130" Fill="#E2E8F0"/> <Button Width="130" Height="130"
Command="{Binding StartProtocolCommand}"
<Button Command="{Binding StartProtocolCommand}" Background="#7C3AED" BorderBrush="#6D28D9" BorderThickness="0"
Width="110" Height="110" materialDesign:ElevationAssist.Elevation="Dp6">
Style="{StaticResource MaterialDesignFloatingActionDarkButton}"
Background="#1E293B"
BorderBrush="#334155"
BorderThickness="2"
materialDesign:ElevationAssist.Elevation="Dp6"
materialDesign:RippleAssist.Feedback="#38BDF8">
<Button.Resources> <Button.Resources>
<Style TargetType="Border"> <Style TargetType="Border">
<Setter Property="CornerRadius" Value="55"/> <Setter Property="CornerRadius" Value="65"/>
</Style> </Style>
</Button.Resources> </Button.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<materialDesign:PackIcon Kind="Play" <materialDesign:PackIcon Kind="Power" Width="44" Height="44"
Width="48" Height="48" Foreground="#FFFFFF" HorizontalAlignment="Center"/>
Foreground="#38BDF8" <TextBlock Text="START" Margin="0,6,0,0"
HorizontalAlignment="Center" FontSize="14" FontWeight="Bold"
VerticalAlignment="Center" Foreground="#FFFFFF" HorizontalAlignment="Center"/>
Margin="4,0,0,0"/> </StackPanel>
</Button> </Button>
</Grid> </Grid>
<StackPanel Grid.Row="2" HorizontalAlignment="Center" Margin="0,0,0,8"> <!-- 底部 -->
<TextBlock Text="READY" <StackPanel Grid.Row="2" HorizontalAlignment="Center">
FontSize="13" <Border Background="#E0E7FF" CornerRadius="8" Padding="16,6">
FontWeight="Black" <StackPanel Orientation="Horizontal">
Foreground="#10B981" <Ellipse Width="8" Height="8" Fill="#4F46E5" Margin="0,0,6,0"/>
HorizontalAlignment="Center"/> <TextBlock Text="系统准备中" FontSize="13"
<TextBlock Text="点击按钮加载测试序列" FontWeight="Bold" Foreground="#4338CA"/>
FontSize="12" </StackPanel>
Foreground="#94A3B8" </Border>
Margin="0,4,0,0" <TextBlock Text="点击按钮加载测试序列" Margin="0,10,0,0"
HorizontalAlignment="Center"/> FontSize="12" Foreground="#94A3B8" HorizontalAlignment="Center"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</materialDesign:Card> </Border>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -8,7 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.215-preview13" /> <PackageReference Include="SqlSugarCore" Version="5.1.4.215-preview13" />
<PackageReference Include="System.IO.Ports" Version="11.0.0-preview.4.26230.115" /> <PackageReference Include="System.IO.Ports" Version="8.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -214,15 +214,15 @@ namespace UIShare.GlobalVariable
// IOGroup 初始化:从已实例化的设备中取出前两个 IOBoard 实例组建 IOBoardGroup // IOGroup 初始化:从已实例化的设备中取出前两个 IOBoard 实例组建 IOBoardGroup
try try
{ {
var ioBoards = DeviceMap.Values.OfType<IOBoard>().Take(2).ToList(); var ioBoards = DeviceMap.Values.OfType<IOBoard>().Take(3).ToList();
if (ioBoards.Count == 2) if (ioBoards.Count == 3)
{ {
IOGroup = new IOBoardGroup(ioBoards[0], ioBoards[1]); IOGroup = new IOBoardGroup(ioBoards[0], ioBoards[1], ioBoards[2]);
LoggerHelper.Info($"IOBoardGroup 已初始化Board1={ioBoards[0].GetType().Name}, Board2={ioBoards[1].GetType().Name}。"); LoggerHelper.Info($"IOBoardGroup 已初始化Board1={ioBoards[0].GetType().Name}, Board2={ioBoards[1].GetType().Name}, Board3={ioBoards[2].GetType().Name}。");
} }
else else
{ {
LoggerHelper.Warn($"IOBoardGroup 初始化跳过:需要 2 个 IOBoard 实例,当前仅找到 {ioBoards.Count} 个。"); LoggerHelper.Warn($"IOBoardGroup 初始化跳过:需要 3 个 IOBoard 实例,当前仅找到 {ioBoards.Count} 个。");
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -53,22 +53,6 @@ namespace UIShare.GlobalVariable
IsEditable=false IsEditable=false
}, },
new ParameterVM new ParameterVM
{
Category = ParameterCategory.Input,
Type = typeof(int),
Name = "直流负载通道",
Value = 1,
IsEditable=false
},
new ParameterVM
{
Category = ParameterCategory.Input,
Type = typeof(int),
Name = "交流电源通道",
Value = 1,
IsEditable=false
},
new ParameterVM
{ {
Category = ParameterCategory.Input, Category = ParameterCategory.Input,
Type = typeof(int), Type = typeof(int),
@@ -113,63 +97,112 @@ namespace UIShare.GlobalVariable
//{ //{
// new DeviceInfoVM // new DeviceInfoVM
// { // {
// DeviceName = "IT7800E", // DeviceName = "Chroma61800",
// DeviceType = "IT7800E", // DeviceType = "Chroma61800",
// Remark = "交流可编程电源供应器", // Remark = "可编程交流电源",
// ConnectionType = "Tcp", // ConnectionType = "Tcp",
// IsEnabled = true, // IsEnabled = true,
// IsConnected = false // IsConnected = false
// }, // },
// new DeviceInfoVM
// new DeviceInfoVM // {
// { // DeviceName = "DG1000Z",
// DeviceName = "N36200", // DeviceType = "DG1000Z",
// DeviceType = "N36200", // Remark = "信号发生器",
// Remark = "宽范围可编程直流电源", // ConnectionType = "Tcp",
// ConnectionType = "Tcp", // IsEnabled = true,
// IsEnabled = true, // IsConnected = false
// IsConnected = false // },
// }, // new DeviceInfoVM
// {
// new DeviceInfoVM // DeviceName = "IT6015C",
// { // DeviceType = "IT6015C",
// DeviceName = "N36600", // Remark = "低压直流源载一体机",
// DeviceType = "N36600", // ConnectionType = "Tcp",
// Remark = "便携式宽范围可编程直流电源", // IsEnabled = true,
// ConnectionType = "Tcp", // IsConnected = false
// IsEnabled = false, // },
// IsConnected = false // new DeviceInfoVM
// }, // {
// DeviceName = "IT6036C",
// new DeviceInfoVM // DeviceType = "IT6036C",
// { // Remark = "高压直流源载一体机",
// DeviceName = "N69200", // ConnectionType = "Tcp",
// DeviceType = "N69200", // IsEnabled = true,
// Remark = "可编程直流电子负载", // IsConnected = false
// ConnectionType = "Tcp", // },
// IsEnabled = true, // new DeviceInfoVM
// IsConnected = false // {
// }, // DeviceName = "IT6720",
// DeviceType = "IT6720",
// new DeviceInfoVM // Remark = "低压电源",
// { // ConnectionType = "Tcp",
// DeviceName = "SDS2000X_HD", // IsEnabled = true,
// DeviceType = "SDS2000X_HD", // IsConnected = false
// Remark = "数字存储示波器", // },
// ConnectionType = "Tcp", // new DeviceInfoVM
// IsEnabled = true, // {
// IsConnected = false // DeviceName = "MCc06U",
// }, // DeviceType = "MCc06U",
// Remark = "水冷机",
// new DeviceInfoVM // ConnectionType = "Tcp",
// { // IsEnabled = true,
// DeviceName = "SPAW7000", // IsConnected = false
// DeviceType = "SPAW7000", // },
// Remark = "功率分析记录仪", // new DeviceInfoVM
// ConnectionType = "Tcp", // {
// IsEnabled = true, // DeviceName = "PW8001",
// IsConnected = false // DeviceType = "PW8001",
// } // Remark = "功率分析记录仪",
//}; // ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// },
// new DeviceInfoVM
// {
// DeviceName = "RLT1000",
// DeviceType = "RLT1000",
// Remark = "环境箱",
// ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// },
// new DeviceInfoVM
// {
// DeviceName = "TektronixMSO",
// DeviceType = "TektronixMSO",
// Remark = "示波器",
// ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// },
// new DeviceInfoVM
// {
// DeviceName = "IOBoard1",
// DeviceType = "IOBoard",
// Remark = "IO板卡1",
// ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// },
// new DeviceInfoVM
// {
// DeviceName = "IOBoard2",
// DeviceType = "IOBoard",
// Remark = "IO板卡2",
// ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// },
// new DeviceInfoVM
// {
// DeviceName = "IOBoard3",
// DeviceType = "IOBoard",
// Remark = "IO板卡3",
// ConnectionType = "Tcp",
// IsEnabled = true,
// IsConnected = false
// }
//};
} }
} }

Binary file not shown.

Binary file not shown.

View File

@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<!-- 配置 ViKey.dll 复制到输出目录 -->
<None Include="ViKey.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -1,252 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace
{
public enum VikeyType
{
ViKeyAPP = 0, //实用型加密狗
ViKeySTD = 1, //标准型加密狗
ViKeyNET = 2, //网络型加密狗
ViKeyPRO = 3, //专业型加密狗
ViKeyWEB = 4, //身份认证型加密狗
ViKeyTIME = 5, //时钟型加密狗,内部独立时钟
ViKeyMultiFunctional = 0x0A, //多功能加密狗 支持软件加密 支持文档加密
ViKeyMultiFunctionalTime = 0x0B, //多功能时钟加密狗
ViKeyInvalid //无效类型
};
public enum : uint
{
/// <summary>
/// 成功
/// </summary>
VIKEY_SUCCESS = 0x00000000,
/// <summary>
/// 没有找到ViKey加密锁
/// </summary>
VIKEY_ERROR_NO_VIKEY = 0x80000001,
/// <summary>
/// 密码错误
/// </summary>
VIKEY_ERROR_INVALID_PASSWORD = 0x80000002,
/// <summary>
/// 请先查找加密锁
/// </summary>
VIKEY_ERROR_NEED_FIND = 0x80000003,
/// <summary>
/// 无效的句柄
/// </summary>
VIKEY_ERROR_INVALID_INDEX = 0x80000004,
/// <summary>
/// 数值错误
/// </summary>
VIKEY_ERROR_INVALID_VALUE = 0x80000005,
/// <summary>
/// 秘钥无效
/// </summary>
VIKEY_ERROR_INVALID_KEY = 0x80000006,
/// <summary>
/// 读取信息错误
/// </summary>
VIKEY_ERROR_GET_VALUE = 0x80000007,
/// <summary>
/// 设置信息错误
/// </summary>
VIKEY_ERROR_SET_VALUE = 0x80000008,
/// <summary>
/// 没有机会
/// </summary>
VIKEY_ERROR_NO_CHANCE = 0x80000009,
/// <summary>
/// 权限不足
/// </summary>
VIKEY_ERROR_NO_TAUTHORITY = 0x8000000A,
/// <summary>
/// 地址或长度错误
/// </summary>
VIKEY_ERROR_INVALID_ADDR_OR_SIZE = 0x8000000B,
/// <summary>
/// 获取随机数错误
/// </summary>
VIKEY_ERROR_RANDOM = 0x8000000C,
/// <summary>
/// 获取种子错误
/// </summary>
VIKEY_ERROR_SEED = 0x8000000D,
/// <summary>
/// 通信错误
/// </summary>
VIKEY_ERROR_CONNECTION = 0x8000000E,
/// <summary>
/// 算法或计算错误
/// </summary>
VIKEY_ERROR_CALCULATE = 0x8000000F,
/// <summary>
/// 计数器错误
/// </summary>
VIKEY_ERROR_MODULE = 0x80000010,
/// <summary>
/// 产生密码错误
/// </summary>
VIKEY_ERROR_GENERATE_NEW_PASSWORD = 0x80000011,
/// <summary>
/// 加密数据错误
/// </summary>
VIKEY_ERROR_ENCRYPT_FAILED = 0x80000012,
/// <summary>
/// 解密数据错误
/// </summary>
VIKEY_ERROR_DECRYPT_FAILED = 0x80000013,
/// <summary>
/// ViKey加密锁已经被锁定
/// </summary>
VIKEY_ERROR_ALREADY_LOCKED = 0x80000014,
/// <summary>
/// 无效的命令
/// </summary>
VIKEY_ERROR_UNKNOWN_COMMAND = 0x80000015,
/// <summary>
/// 当前ViKey加密锁不支持此功能
/// </summary>
VIKEY_ERROR_NO_SUPPORT = 0x80000016,
/// <summary>
/// 发生异常
/// </summary>
VIKEY_ERROR_CATCH = 0x80000017,
/// <summary>
/// 未知错误
/// </summary>
VIKEY_ERROR_UNKNOWN_ERROR = 0xFFFFFFFF,
}
public class API类
{
/// <summary>
/// 查找加密锁。使用其他API前必须先调用次函数。
/// </summary>
/// <param name="pdwCount">[out] 如果查找到系统中存在加密狗,返回查找到加密狗的个数</param>
/// <returns>0表示系统中存在ViKey加密狗。</returns>
[DllImport("ViKey.dll")]
public static extern uint VikeyFind(ref uint pdwCount);
/// <summary>
/// 获取加密狗的硬件ID,加密狗的硬件ID是加密狗的唯一标识每个加密狗的硬件ID都不一样。
/// </summary>
/// <param name="Index">[in]指定要操作加密狗的序号</param>
/// <param name="pdwHID">[out]返回加密狗的硬件ID</param>
/// <returns></returns>
[DllImport("ViKey.dll")]
public static extern uint VikeyGetHID(ushort Index, out uint pdwHID);
/// <summary>
/// 获取加密狗的当前权限
/// </summary>
/// <param name="Index">指定要操作加密狗的序号</param>
/// <param name="pLevel">返回加密狗的当前权限 0表示加密狗尚未登录 1表示加密狗是用户权限 2表示加密狗是管理员权限</param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyGetLevel(ushort Index, out byte pLevel);
/// <summary>
/// 以用户权限登录加密狗
/// </summary>
/// <param name="Index"></param>
/// <param name="pUserPassushort"></param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern uint VikeyUserLogin(ushort Index, Byte[] pUserPassword);
/// <summary>
/// 以管理员权限登录加密狗
/// </summary>
/// <param name="Index"></param>
/// <param name="pAdminPassword"></param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern uint VikeyAdminLogin(ushort Index, Byte[] pAdminPassword);
/// <summary>
/// 注销登录加密狗 注销后的加密狗权限为0
/// </summary>
/// <param name="Index">[in]指定要操作加密狗的序号</param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern uint VikeyLogoff(ushort Index);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SVikeyTime
{
public byte cYear;
public byte cMonth;
public byte cDay;
public byte cHour;
public byte cMinute;
public byte cSecond;
}
/// <summary>
/// 获取时钟型加密狗中的内部独立时间
/// </summary>
/// <param name="Index"></param>
/// <param name="pTime"></param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyGetTime(ushort Index, out SVikeyTime pTime);
/// <summary>
/// 获取时钟型加密狗中的到期时间
/// </summary>
/// <param name="Index"></param>
/// <param name="pTime"></param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyGetValidTime(ushort Index, out SVikeyTime pTime);
/// <summary>
/// 检测时钟型加密狗的时钟功能是否到期
/// </summary>
/// <param name="Index">[in]指定要操作加密狗的序号</param>
/// <param name="pIsValid">[out]返回是否到期结果 1表示没有到期 0表示已经到期</param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyCheckValidTime(ushort Index, out byte pIsValid);
/// <summary>
/// 从加密狗中的获取4个双字的随机数数据
/// </summary>
/// <param name="Index">指定要操作加密狗的序号</param>
/// <param name="pwRandom1"></param>
/// <param name="pwRandom2"></param>
/// <param name="pwRandom3"></param>
/// <param name="pwRandom4"></param>
/// <returns></returns>
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint ViKeyRandom(ushort Index,
out ushort pwRandom1, out ushort pwRandom2, out ushort pwRandom3, out ushort pwRandom4);
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyReadData(ushort Index, ushort Addr, ushort Length, byte[] buffer);
[DllImport("ViKey.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint VikeyWriteData(ushort Index, ushort Addr, ushort Length, byte[] buffer);
/// <summary>
/// // 7.4.6版本(含)以后的加密狗必须在查找加密狗前调用VikeySetUserSN并输入正确的UserSN后面才能查找到所属用户的加密狗
// 7.4.6版本以前的加密狗可以忽略此函数 不用调用此函数
// UserSN在ViKey加密管理工具的->帮助中心页面->复制出来
/// </summary>
/// <param name="pUserSN"></param>
/// <returns></returns>
[DllImport("ViKey")]
public static extern uint VikeySetUserSN(Byte[] pUserSN);
/// <summary>
/// //设置加密狗ApiKey 硬件版本为7.4.5(含)以后的加密狗必须要调用此接口才能登录
//ApiKey 是由ViKey加密管理工具的->加密狗管理->密钥设置页面 通过Api密钥种子生成的
/// </summary>
/// <param name="Index"></param>
/// <param name="pApiKey"></param>
/// <returns></returns>
[DllImport("ViKey")]
public static extern uint VikeySetApiKey(ushort Index, Byte[] pApiKey);
[DllImport("ViKey")]
public static extern uint VikeyUninitialization();
}
}

View File

@@ -1,145 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace
{
public class
{
public static void Uninitialization()
{
API类.VikeyUninitialization();
}
// 保留原有方法,但添加新方法
public static int (ushort Index, string , string )
{
// 保持不变
uint = 0;
var re = API类.VikeyFind(ref );
if (re != 0) return -1;
API类.VikeyAdminLogin(Index, Encoding.Default.GetBytes());
SHA256 sHA256 = SHA256.Create();
API类.VikeyGetHID(Index, out uint pid);
byte[] HID = BitConverter.GetBytes(pid);
var pswd = Encoding.UTF8.GetBytes();
byte[] = HID.Concat(pswd).ToArray();
var = sHA256.ComputeHash();
API类.VikeyWriteData(Index, 0, 32, );
byte[] randomBytes = new byte[96];
Random random = new Random();
random.NextBytes(randomBytes);
API类.VikeyWriteData(Index, 32, 96, randomBytes);
return 0;
}
/// <summary>
/// 新方法:仅检查加密狗时间,不验证密码和数据
/// </summary>
public static int ()
{
uint = 0;
var re = API类.VikeyFind(ref );
if (re != 0 || == 0) return -1;
// 直接返回第一个找到的加密狗
return 0;
}
/// <summary>
/// 原有方法:寻找加密狗并进行完整验证
/// </summary>
public static int (string ,string )
{
uint = 0;
var re = API类.VikeyFind(ref );
if (re != 0) return -1;
int = -1;
for (ushort i = 0; i < ; i++)
{
re = API类.VikeyUserLogin(i, Encoding.Default.GetBytes());
if ((i, )) return i;
}
return (int);
}
public static bool (ushort Index, string )
{
// 保持不变
SHA256 sHA256 = SHA256.Create();
API类.VikeyGetHID(Index, out uint pid);
byte[] HID = BitConverter.GetBytes(pid);
var pswd = Encoding.UTF8.GetBytes();
byte[] = HID.Concat(pswd).ToArray();
var = sHA256.ComputeHash();
var buffer = new byte[32];
API类.VikeyReadData(Index, 0, 32, buffer);
return buffer.SequenceEqual();
}
public static TimeSpan (int )
{
ushort = (ushort);
try
{
// 读时间前必须先登录(用户权限即可)
uint re = API类.VikeyUserLogin(, Encoding.Default.GetBytes("11111111"));
if (re != 0)
{
Console.WriteLine($"VikeyUserLogin 失败: 0x{re:X}");
return TimeSpan.Zero;
}
re = API类.VikeyGetTime(, out API类.SVikeyTime );
if (re != 0)
{
Console.WriteLine($"VikeyGetTime 失败: 0x{re:X}");
return TimeSpan.Zero;
}
re = API类.VikeyGetValidTime(, out API类.SVikeyTime );
if (re != 0)
{
Console.WriteLine($"VikeyGetValidTime 失败: 0x{re:X}");
return TimeSpan.Zero;
}
re = API类.VikeyCheckValidTime(, out byte isValid);
Console.WriteLine($"VikeyCheckValidTime: ret=0x{re:X}, isValid={isValid}");
// cYear 是 byteViKey 用距 2000 年的偏移量
DateTime _现在时间 = new DateTime(.cYear + 2000, .cMonth, .cDay,
.cHour, .cMinute, .cSecond);
DateTime _到期时间 = new DateTime(.cYear + 2000, .cMonth, .cDay,
.cHour, .cMinute, .cSecond);
Console.WriteLine($"加密狗当前时间: {_现在时间:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($"加密狗到期时间: {_到期时间:yyyy-MM-dd HH:mm:ss}");
return _到期时间 - _现在时间;
}
catch (Exception ex)
{
Console.WriteLine($"检查剩余时间异常: {ex.Message}");
return TimeSpan.Zero;
}
}
public static uint SN(Byte[] buffer)
{
return API类.VikeySetUserSN(buffer);
}
public static uint APIkey(ushort index, Byte[] buffer)
{
return API类.VikeySetApiKey(index, buffer);
}
}
}

View File

@@ -1,15 +0,0 @@
using System;
using ;
// ═══════════════════════════════════════════════════════
// 刷入脚本(独立运行,不属于 ACP 主程序流程)
// 使用前请先用 ViKey 管理工具设置好密码和到期时间
// ═══════════════════════════════════════════════════════
public class Program
{
public static void Main(string[] args)
{
//写入加密狗(需管理员密码)
.(0, "00000000", "ACP项目");
}
}