Compare commits
6 Commits
850156bfa2
...
21cc593d3f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21cc593d3f | ||
|
|
63af886f26 | ||
|
|
2e74627b31 | ||
|
|
5cfb8af9e4 | ||
|
|
ceae6e3421 | ||
|
|
e90dc805fc |
@@ -9,7 +9,6 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ncalc" Version="1.3.8" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.5-beta1" />
|
||||
<PackageReference Include="System.IO.Ports" Version="11.0.0-preview.4.26230.115" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
using CurveModule.Views;
|
||||
using CurveModule.Views.Dialogs;
|
||||
using System.Reflection;
|
||||
|
||||
namespace CurveModule
|
||||
@@ -14,6 +15,7 @@ namespace CurveModule
|
||||
public void RegisterTypes(IContainerRegistry containerRegistry)
|
||||
{
|
||||
containerRegistry.RegisterForNavigation<CurveRecallView>("CurveRecallView");
|
||||
containerRegistry.RegisterForNavigation<CurveStatisticsView>("CurveStatistics");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
169
DeviceCommand/Devices/Chroma61800.cs
Normal file
169
DeviceCommand/Devices/Chroma61800.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
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 序列参数
|
||||
/// 传入的数组长度必须保持一致,代表 LIST 的每个 Step(SEQ)
|
||||
/// </summary>
|
||||
/// <param name="phase">相位 (1: L1, 2: L2, 3: L3)</param>
|
||||
/// <param name="degrees">各步起始角度 (如 [0, 0, 0])</param>
|
||||
/// <param name="vacStart">各步交流起始电压 (如 [230, 11.5, 230])</param>
|
||||
/// <param name="vacEnd">各步交流结束电压 (如 [230, 11.5, 230])</param>
|
||||
/// <param name="vdcStart">各步直流起始电压</param>
|
||||
/// <param name="vdcEnd">各步直流结束电压</param>
|
||||
/// <param name="freqStart">各步起始频率</param>
|
||||
/// <param name="freqEnd">各步结束频率</param>
|
||||
/// <param name="dwellTimeMs">各步执行时间(单位: 毫秒)</param>
|
||||
public virtual async Task 配置单相List序列(
|
||||
int phase,
|
||||
double[] degrees,
|
||||
double[] vacStart, double[] vacEnd,
|
||||
double[] vdcStart, double[] vdcEnd,
|
||||
double[] freqStart, double[] freqEnd,
|
||||
double[] dwellTimeMs,
|
||||
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);
|
||||
|
||||
// 转换数组为 SCPI 需要的逗号分隔字符串
|
||||
string strDegrees = JoinParameters(degrees);
|
||||
string strVacStart = JoinParameters(vacStart);
|
||||
string strVacEnd = JoinParameters(vacEnd);
|
||||
string strVdcStart = JoinParameters(vdcStart);
|
||||
string strVdcEnd = JoinParameters(vdcEnd);
|
||||
string strFreqStart = JoinParameters(freqStart);
|
||||
string strFreqEnd = JoinParameters(freqEnd);
|
||||
string strDwell = JoinParameters(dwellTimeMs);
|
||||
|
||||
// 下发序列参数
|
||||
// 注意 Chroma 部分指令是 DEGR,部分固件版本是 DEGRee,这里统一使用简写 DEGR,兼容性最好
|
||||
await SendAsync($"LIST:DEGR {strDegrees}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:VOLT:AC:STAR {strVacStart}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:VOLT:AC:END {strVacEnd}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:VOLT:DC:STAR {strVdcStart}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:VOLT:DC:END {strVdcEnd}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:FREQ:STAR {strFreqStart}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:FREQ:END {strFreqEnd}{ScpiDelimiter}", ct);
|
||||
await SendAsync($"LIST:DWEL {strDwell}{ScpiDelimiter}", ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:将 double 数组转换为逗号分隔的字符串,确保小数点格式正确
|
||||
/// </summary>
|
||||
private string JoinParameters(double[] values)
|
||||
{
|
||||
return string.Join(",", values.Select(v => v.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
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// 信号源 一拖三
|
||||
/// </summary>
|
||||
#region 枚举定义
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// IO板卡 一对一由于板卡映射不完全一样采用IOGroup
|
||||
/// </summary>
|
||||
//[ACPCommand]
|
||||
public class IOBoard : ModbusTcp
|
||||
{
|
||||
|
||||
@@ -8,7 +8,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 低压直流源载一体机 一对一
|
||||
/// </summary>
|
||||
[ACPCommand]
|
||||
public class IT6015C : Tcp
|
||||
{
|
||||
|
||||
@@ -8,6 +8,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
///高压直流源载一体机 一对一
|
||||
/// </summary>
|
||||
#region 枚举定义
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,17 +9,19 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// 低压电源 一拖三
|
||||
/// </summary>
|
||||
[ACPCommand]
|
||||
public class IT6720 : Serial_Port // 直接继承您提供的 Serial_Port 基类
|
||||
public class IT6720: Serial_Port // 直接继承您提供的 Serial_Port 基类
|
||||
{
|
||||
// 帧格式:同步头 AA + 地址 + 命令字 + 数据[4-25] + 校验和,共 26 字节
|
||||
private const byte FrameSyncByte = 0xAA;
|
||||
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 核心协议封装(不变)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
20
DeviceCommand/Devices/MCc06U.cs
Normal file
20
DeviceCommand/Devices/MCc06U.cs
Normal 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 MCc06U:ModbusTcp
|
||||
{
|
||||
public MCc06U(TcpConfig config) : base(config)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
using Common;
|
||||
using Common.Attributes;
|
||||
using DeviceCommand.Base;
|
||||
using Model.Models;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Device
|
||||
{
|
||||
/// <summary>
|
||||
/// 功率分析仪(型号:HIOKI PW8001)
|
||||
/// 功率分析仪(型号:HIOKI PW8001) 一拖三
|
||||
/// </summary>
|
||||
|
||||
public class PW8001 : Tcp
|
||||
@@ -15,9 +16,8 @@ namespace DeviceCommand.Device
|
||||
/// <summary>
|
||||
/// 构造函数:SCPI 通信使用端口 23
|
||||
/// </summary>
|
||||
public PW8001()
|
||||
public PW8001(TcpConfig config) : base(config)
|
||||
{
|
||||
Port = 23;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
206
DeviceCommand/Devices/RLT1000.cs
Normal file
206
DeviceCommand/Devices/RLT1000.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace DeviceCommand.Devices
|
||||
{
|
||||
/// <summary>
|
||||
/// 示波器 一拖三
|
||||
/// </summary>
|
||||
#region 枚举定义
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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<IT7800EView>();
|
||||
/// (view.DataContext as IT7800EViewModel)?.Initialize("IT7800E");
|
||||
/// _eventAggregator.GetEvent<AddDialogTabEvent>().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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -53,22 +53,6 @@ namespace UIShare.GlobalVariable
|
||||
IsEditable=false
|
||||
},
|
||||
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,
|
||||
Type = typeof(int),
|
||||
@@ -113,63 +97,112 @@ namespace UIShare.GlobalVariable
|
||||
//{
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "IT7800E",
|
||||
// DeviceType = "IT7800E",
|
||||
// Remark = "交流可编程电源供应器",
|
||||
// DeviceName = "Chroma61800",
|
||||
// DeviceType = "Chroma61800",
|
||||
// Remark = "可编程交流电源",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "N36200",
|
||||
// DeviceType = "N36200",
|
||||
// Remark = "宽范围可编程直流电源",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "N36600",
|
||||
// DeviceType = "N36600",
|
||||
// Remark = "便携式宽范围可编程直流电源",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = false,
|
||||
// IsConnected = false
|
||||
// },
|
||||
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "N69200",
|
||||
// DeviceType = "N69200",
|
||||
// Remark = "可编程直流电子负载",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "SDS2000X_HD",
|
||||
// DeviceType = "SDS2000X_HD",
|
||||
// Remark = "数字存储示波器",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "SPAW7000",
|
||||
// DeviceType = "SPAW7000",
|
||||
// Remark = "功率分析记录仪",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// }
|
||||
//};
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "DG1000Z",
|
||||
// DeviceType = "DG1000Z",
|
||||
// Remark = "信号发生器",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "IT6015C",
|
||||
// DeviceType = "IT6015C",
|
||||
// Remark = "低压直流源载一体机",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "IT6036C",
|
||||
// DeviceType = "IT6036C",
|
||||
// Remark = "高压直流源载一体机",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "IT6720",
|
||||
// DeviceType = "IT6720",
|
||||
// Remark = "低压电源",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "MCc06U",
|
||||
// DeviceType = "MCc06U",
|
||||
// Remark = "水冷机",
|
||||
// ConnectionType = "Tcp",
|
||||
// IsEnabled = true,
|
||||
// IsConnected = false
|
||||
// },
|
||||
// new DeviceInfoVM
|
||||
// {
|
||||
// DeviceName = "PW8001",
|
||||
// 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
|
||||
// }
|
||||
//};
|
||||
}
|
||||
}
|
||||
|
||||
BIN
加密狗/ViKey.dll
BIN
加密狗/ViKey.dll
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||
252
加密狗/加密狗API类.cs
252
加密狗/加密狗API类.cs
@@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
145
加密狗/加密狗驱动类.cs
145
加密狗/加密狗驱动类.cs
@@ -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 是 byte,ViKey 用距 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using 加密狗;
|
||||
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// 刷入脚本(独立运行,不属于 ACP 主程序流程)
|
||||
// 使用前请先用 ViKey 管理工具设置好密码和到期时间
|
||||
// ═══════════════════════════════════════════════════════
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
//写入加密狗(需管理员密码)
|
||||
加密狗驱动类.写入加密狗(0, "00000000", "ACP项目");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user