QianTools 2.6.0
dotnet add package QianTools --version 2.6.0
NuGet\Install-Package QianTools -Version 2.6.0
<PackageReference Include="QianTools" Version="2.6.0" />
<PackageVersion Include="QianTools" Version="2.6.0" />
<PackageReference Include="QianTools" />
paket add QianTools --version 2.6.0
#r "nuget: QianTools, 2.6.0"
#:package QianTools@2.6.0
#addin nuget:?package=QianTools&version=2.6.0
#tool nuget:?package=QianTools&version=2.6.0
QianTools
.NET 工业自动化与系统管理工具库,支持 net6.0 / net8.0。
作者:钱军涛 | 公司:浪涛科技有限公司 | 许可证:MIT
安装
dotnet add package QianTools
项目结构
QianTools/
├── Core/ # 基础设施(日志、配置)
│ ├── LogManager.cs # 日志管理器(支持 ILogger 接口、级别过滤、文件滚动)
│ ├── ILogger.cs # 日志接口
│ ├── LogLevel.cs # 日志级别枚举
│ ├── ConfigManager.cs # 配置管理器(JSON/XML,策略模式可扩展)
│ ├── IConfigParser.cs # 配置解析器接口
│ ├── JsonParser.cs # JSON 解析器
│ └── XmlParser.cs # XML 解析器
├── Helpers/ # 通用工具类
│ ├── AllTools.cs # 统一外观入口(FileTools / CryptoTools / NetworkTools)
│ ├── CryptoHelper.cs # 加密与哈希(SHA256、MD5)
│ ├── FileHelper.cs # 文件/目录复制与删除
│ └── NetworkHelper.cs # HTTP 文件下载
├── System/ # 系统硬件信息
│ ├── SystemHardwareInfo.cs # 串口、磁盘、CPU、操作系统、网络适配器
│ └── MachineCodeGenerator.cs # 机器码生成与授权验证
├── Plc/ # PLC 通信
│ ├── MelsecPlcService.cs # 三菱 FX 底层通信服务(ASCII 协议)
│ └── MelsecPlcHelper.cs # 上层静态封装
├── EtherNetIP/ # EtherNet/IP 与 Modbus TCP 通信
│ ├── EtherNetIPClient.cs # EtherNet/IP 显式消息客户端
│ ├── Encapsulation.cs # 封装协议头操作
│ ├── CipMessage.cs # CIP 协议消息构建与解析
│ ├── EdsAssemblyReader.cs # Assembly 数据读取器
│ └── Ex1121ModbusClient.cs# EX-1121 Modbus TCP IO 客户端
├── Ezd/ # JCZ激光打标板卡控制
│ ├── EzdClient.cs # 高层封装(单例),生命周期/异步/业务级 API
│ ├── EzdNative.cs # P/Invoke 底层接口声明
│ └── EzdErrorCode.cs # 故障码定义与中文消息
├── Application/ # 应用层服务
│ ├── AutoStartManager.cs # 注册表开机自启动
│ ├── ShortcutCreator.cs # 桌面快捷方式
│ ├── VerifyInstallation.cs# 软件安装与验证
│ ├── MesClient.cs # MES HTTP 客户端
│ └── MesTcpClient.cs # MES TCP 通信客户端(JSON 换行协议)
├── QianTools.csproj
└── README.md
功能模块
Core — 基础设施
日志管理 — LogManager
// 使用静态方法(默认单例)
LogManager.Info("设备启动成功");
LogManager.Error($"通信失败: {ex.Message}");
// 自定义日志路径和文件滚动
LogManager.Initialize(@"D:\AppLogs\myapp.log");
LogManager.MinLogLevel = LogLevel.Debug;
LogManager.MaxFileSizeBytes = 10 * 1024 * 1024; // 10MB 自动滚动
// 通过 ILogger 接口注入(支持单元测试)
ILogger logger = LogManager.Instance;
logger.LogInfo("消息");
配置管理 — ConfigManager
支持 JSON(默认)和 XML 两种格式,策略模式可扩展。
// ===== JSON 模式(默认) =====
var cfg = new ConfigManager(); // 或 ConfigManager.Default
cfg.Set("AppSettings", new AppSettings { Theme = "Dark" });
var settings = cfg.Get<AppSettings>("AppSettings"); // 从内存读取
cfg.Save(); // 持久化到文件
cfg.Load(); // 从文件加载到内存
cfg.Remove("AppSettings");
// ===== XML 模式 =====
var xmlCfg = new ConfigManager(useXml: true, customPath: @"D:\MyApp\Config");
xmlCfg.Set("AppSettings", new AppSettings { Theme = "Dark" });
xmlCfg.Save(); // → D:\MyApp\Config\config.xml
// ===== 错误监听 =====
ConfigManager.OnError += msg => Console.WriteLine(msg);
Helpers — 通用工具
统一入口 — AllTools
// 通过 AllTools 静态子类访问
AllTools.FileTools.CopyDirectory(@"C:\Source", @"D:\Backup", overwrite: true);
AllTools.FileTools.DeleteDirectory(@"C:\Temp\OldData");
string sha256 = AllTools.CryptoTools.ComputeSha256Hash("hello");
string md5 = AllTools.CryptoTools.ComputeMd5Hash("hello");
await AllTools.NetworkTools.DownloadFileAsync(url, destinationPath);
// Excel 操作
AllTools.ExcelTools.WriteToExcel(dataList, @"D:\data.xlsx", "Sheet1");
DataTable dt = AllTools.ExcelTools.ReadToDataTable(@"D:\data.xlsx");
List<MyClass> list = AllTools.ExcelTools.ReadToList<MyClass>(@"D:\data.xlsx");
文件操作 / 加密 / 网络 / Excel
// 文件
FileHelper.CopyDirectory(@"C:\Source", @"D:\Backup", overwrite: true);
FileHelper.DeleteDirectory(@"C:\Temp\OldData");
// 加密
string sha256 = CryptoHelper.ComputeSha256Hash("hello");
string md5 = CryptoHelper.ComputeMd5Hash("hello");
// 网络
await NetworkHelper.DownloadFileAsync("https://example.com/file.zip", @"D:\Download\file.zip");
// Excel(基于 ClosedXML,仅 .xlsx)
ExcelHelper.WriteToExcel(dataList, @"D:\data.xlsx", "Sheet1");
ExcelHelper.WriteDataTable(dataTable, @"D:\data.xlsx", "Sheet1");
DataTable dt = ExcelHelper.ReadToDataTable(@"D:\data.xlsx");
List<MyClass> list = ExcelHelper.ReadToList<MyClass>(@"D:\data.xlsx");
string[] sheets = ExcelHelper.GetSheetNames(@"D:\data.xlsx");
System — 系统信息
硬件信息 — SystemHardwareInfo
string ports = SystemHardwareInfo.GetPort(); // 串口列表
string disks = SystemHardwareInfo.GetDiskInfo(); // 磁盘信息
string cpu = SystemHardwareInfo.GetCpuInfo(); // CPU 信息
string os = SystemHardwareInfo.GetOsInfo(); // 操作系统
string network = SystemHardwareInfo.GetNetworkAdapterInfo(); // 网络适配器
机器码与授权 — MachineCodeGenerator
// 生成 24 位机器码(CPU + 硬盘 + 网卡)
string machineCode = MachineCodeGenerator.GenerateMachineCode();
string hash = MachineCodeGenerator.GetMachineCodeHash(); // SHA256 大写
// ── 无口令模式 ──
// 授权方生成注册码
string licenseKey = MachineCodeGenerator.GenerateLicenseKey(
machineCode, new DateTime(2026, 12, 31));
// 用户端验证(返回 LicenseResult,可解构为 tuple)
var (isValid, expiryDate) = MachineCodeGenerator.ValidateLicenseKey(licenseKey, machineCode);
// ── 口令保护模式 ──
string key = MachineCodeGenerator.GenerateLicenseKey(
machineCode, new DateTime(2026, 12, 31), password: "admin@123");
var (ok, expiry) = MachineCodeGenerator.ValidateLicenseKey(key, machineCode, "admin@123");
// ── 详细诊断 ──
LicenseResult result = MachineCodeGenerator.ValidateLicenseKey(key, machineCode);
// result.IsValid / result.ExpiryDate / result.ErrorReason
// result.MachineCodeOk / result.DateOk / result.PasswordOk / result.HasPassword
Plc — PLC 通信
MelsecPlcHelper
支持三菱 FX 系列(X/Y/M/S/T/C/D 元件),ASCII 协议串口通信。
// 连接 PLC
MelsecPlcHelper.Connect("COM1", baudRate: 9600);
// 读取
bool x0 = MelsecPlcHelper.ReadX(0);
bool y1 = MelsecPlcHelper.ReadY(1);
bool m10 = MelsecPlcHelper.ReadM(10);
short d100 = MelsecPlcHelper.ReadD(100);
short t5 = MelsecPlcHelper.ReadT(5);
// 写入
MelsecPlcHelper.WriteY(1, true);
MelsecPlcHelper.WriteM(10, false);
MelsecPlcHelper.WriteD(100, 12345);
// 轮询 D 寄存器
MelsecPlcHelper.OnDataUpdated += snapshot => {
Console.WriteLine($"D100 = {snapshot.D[100]}");
};
MelsecPlcHelper.StartPolling(new[] { 100, 101, 102 }, intervalMs: 100);
// 日志监听
MelsecPlcHelper.OnLog += (msg, level) => Console.WriteLine($"[{level}] {msg}");
// 断开
MelsecPlcHelper.Shutdown();
EtherNetIP — EtherNet/IP 与 Modbus TCP 通信
命名空间: QianTools.EtherNetIP
支持 EX-1121(DECOWELL/Keyence 兼容)远程 IO 模块,提供 EtherNet/IP 显式消息和 Modbus TCP 两种通信方式。
EtherNet/IP 客户端
using QianTools.EtherNetIP;
var client = new EtherNetIPClient("192.168.1.100", timeout: TimeSpan.FromSeconds(3));
await client.ConnectAsync();
// 读取输入 Assembly(Instance 100)— 过程镜像输入数据
byte[] inputData = await client.ReadInputAssemblyAsync();
var reader = new EdsAssemblyReader(inputData);
// 按偏移量读取数据
ushort ch0 = reader.ReadUInt16(0); // 16 位无符号
short ch1 = reader.ReadInt16(2); // 16 位有符号
uint ch2 = reader.ReadUInt32(4); // 32 位无符号
bool bit0 = reader.ReadBool(0, 0); // 位读取
// 读取输出 Assembly(Instance 101)
byte[] outputData = await client.ReadOutputAssemblyAsync();
// 显式消息:GetAttributeSingle
byte[] data = await client.GetAttributeAsync(
classId: 0x04, instanceId: 100, attributeId: 0x03);
// 显式消息:SetAttributeSingle
await client.SetAttributeAsync(0x04, 101, 0x03, new byte[] { 0x00, 0x01 });
// Keyence / Decowell EDS 参数读取(服务 0x4C)
ushort param = await client.ReadParameterUInt16Async(slot: 1, paramNo: 512);
short param2 = await client.ReadParameterInt16Async(slot: 1, paramNo: 513);
uint param3 = await client.ReadParameterUInt32Async(slot: 1, paramNo: 514);
client.Disconnect();
Modbus TCP 客户端
var config = Ex1121Config.Default; // DI=0xA000, DO=0xB000, 可自定义
using var io = new Ex1121ModbusClient("169.254.193.30", config, port: 20001);
await io.ConnectAsync();
// 读 DI
ushort[] inputs = await io.ReadInputsAsync();
bool di15 = Ex1121ModbusClient.GetDiBit(inputs, 15);
// 读 DO
ushort[] outputs = await io.ReadOutputsAsync();
bool do0 = Ex1121ModbusClient.GetDoBit(outputs, 0);
// 写 DO
await io.WriteOutputsAsync(0x0001); // DO0 = ON
await io.SetDoBitAsync(3, true); // DO3 = ON
// 自定义寄存器地址读写
ushort[] regs = await io.ReadRegistersAsync(0xA000, 2);
await io.WriteRegistersAsync(0xB000, new ushort[] { 0x0001 });
io.Disconnect();
Application — 应用层服务
MES 通信 — MesClient
// GET 请求
string result = await MesClient.SendGetRequestAsync(
"https://mes-server/api", "production/orders", apiKey: "your-token");
// POST 请求
var data = new { OrderId = "P001", Quantity = 100 };
string response = await MesClient.SendPostRequestAsync(
"https://mes-server/api", "production/report", data, apiKey: "your-token");
MES TCP 通信 — MesTcpClient
基于 TCP + 换行分隔 JSON 协议,支持请求-响应模式和服务器主动推送。线程安全,适合与产线 MES 服务器进行长连接双向通信。
using QianTools.Mes;
var mes = MesTcpClient.Instance;
// ── 超时设置(连接前或连接后均可设置)──
mes.TimeoutMs = 10000; // 请求-响应超时 10 秒
// ── 连接 ──
bool ok = await mes.ConnectAsync("192.168.1.100", 8080); // 使用 TimeoutMs 属性值
// 也可在连接时显式传入超时
bool ok2 = await mes.ConnectAsync("192.168.1.100", 8080, timeoutMs: 3000);
// 连接状态通过事件通知
mes.OnConnectionChanged += connected => Console.WriteLine(connected ? "已连接" : "已断开");
mes.OnMessageReceived += json => Console.WriteLine($"服务器推送: {json}");
mes.OnError += err => Console.WriteLine($"异常: {err}");
// ── 测试连接(发送 ping)──
var (pingOk, pingResp) = await mes.TestAsync();
// ── 上报打标结果 ──
var (resultOk, resultResp) = await mes.SendResultAsync(
productCode: "PC001",
workOrder: "WO-20260523",
productionLine: "L1",
success: true,
entityName: "Text1",
fileName: @"C:\Files\product.ezd");
// ── 通用请求-响应 ──
string? response = await mes.SendAndReceiveAsync("{\"cmd\":\"query_status\"}");
// ── 单向发送(不等待响应)──
await mes.SendAsync("{\"cmd\":\"heartbeat\"}");
// ── 断开 ──
mes.Disconnect();
协议格式:每条 JSON 消息以 \n 结尾。常见消息类型:
| 方向 | cmd | 说明 |
|---|---|---|
| 客户端→服务端 | ping |
心跳/连通性测试 |
| 客户端→服务端 | mark_result |
上报打标结果(含产品代码、工单号、成功/失败等) |
| 服务端→客户端 | start_mark |
要求开始标刻 |
| 服务端→客户端 | select_entity |
切换打标对象 |
| 服务端→客户端 | update_product |
更新生产信息(产品代码/工单号等) |
线程安全:所有 I/O 通过 SemaphoreSlim 串行化。后台读循环自动处理服务器主动推送的消息。
桌面快捷方式 — ShortcutCreator
ShortcutCreator.CreateShortcut("我的应用", @"D:\App\MyApp.exe", arguments: "-start");
开机启动 — AutoStartManager
AutoStartManager.EnsureAutoStart("MyApp", @"D:\App\MyApp.exe", enable: true);
AutoStartManager.EnsureAutoStart("MyApp", null, enable: false); // 取消
安装验证 — VerifyInstallation
// 检查软件是否安装
bool installed = VerifyInstallation.IsSoftwareInstalled("MyApp", version: "2.0");
// 安装程序(需用户确认委托)
VerifyInstallation.Notify = (title, msg) => MessageBox.Show(msg, title) == DialogResult.OK;
await VerifyInstallation.InstallAsync("安装 MyApp", @"D:\Installer", "setup.exe");
Ezd — JCZ激光打标板卡控制
命名空间: QianTools.Ezd
通过 P/Invoke 封装 MarkEzd.dll,提供JCZ激光打标板卡的完整控制能力。支持初始化/关闭、文件加载与保存、实体管理、标刻控制、红光预览、图形编辑、笔号/填充参数设置、扩展轴控制、飞行标刻等功能。
using QianTools.Ezd;
var ezd = EzdClient.Instance;
// ── 初始化与关闭 ──
int ret = ezd.Initialize2(@"C:\Laser\EzCad2", testMode: false);
// 内部自动切换 Environment.CurrentDirectory,确保 DLL 能找到 EZCAD.CFG 和 plug/markcfg*
// ── 文件操作 ──
ezd.LoadEzdFile(@"C:\Files\product.ezd");
ezd.SaveEzdFile(@"C:\Files\output.ezd");
// ── 实体管理 ──
int count = ezd.GetEntityCount();
string name = ezd.GetEntityName(0); // 按索引获取名称
ezd.AddText("Hello", "Text1", 0, 0, 0, 1, 5, 5, "Arial", 0, false, false, false);
ezd.DeleteEntity("Text1");
ezd.MoveEntity("Text1", 10, 5); // 移动对象
ezd.RotateEntity("Text1", 0, 0, Math.PI/4); // 旋转对象
// ── 标刻控制(异步)──
int result = await ezd.MarkAsync(flyMark: false); // 全部标刻
int result = await ezd.MarkEntityAsync("Text1"); // 按实体名标刻
int result = await ezd.MarkLineAsync(0, 0, 50, 50, 1); // 线段标刻
int result = await ezd.MarkPointAsync(100, 100, 1, 1); // 点标刻
ezd.StopMark(); // 停止标刻
// ── 红光预览 ──
int ret = await ezd.RedLightMarkAsync(); // 红光范围框
int ret = await ezd.RedLightContourAsync(); // 红光轮廓
int ret = await ezd.RedLightEntityAsync("Text1", contour: true); // 指定实体红光
// 持续循环红光(需在后台线程执行)
Task.Run(() => ezd.RedLightContourLoop(() => shouldStop));
// ── 预览图获取 ──
IntPtr hBmp = ezd.GetPreviewBitmap(320, 240); // 全局预览
IntPtr hBmp = ezd.GetEntityPreviewBitmap("Text1", 320, 240); // 指定实体预览
// 使用后需释放: EzdNative.DeleteObject(hBmp);
// ── 图形编辑 ──
ezd.GroupEntities("A", "B", "AB", 1); // 合并对象
ezd.UnGroupEntity("AB"); // 分解对象
ezd.CopyEntity("A", "A_copy"); // 复制对象
ezd.HatchEntity("Name", "Source"); // 填充
ezd.ChangeTextByName("Text1", "NewText");// 修改文本内容
// ── 笔号与填充参数 ──
ezd.SetPenParam4(1, "MyPen", 0xFF0000, 100, 20, 30, false, 0, 0, 0, 0, 0, 20000, 50000, 0);
ezd.SetDefaultHatchParam(1, 0.05, 0, 0, false, 45, 0, 0);
ezd.HatchEntity("Target", "Source");
// ── IO 控制 ──
ezd.WritePort(0x00FF); // 写输出口
ezd.ReadPort(out ushort data); // 读输入口
ezd.LaserOn(true); // 激光输出/关闭
ezd.EnableLock(); // 启用锁定输入
ezd.ClearLock(); // 清除锁定
// ── 振镜与扩展轴 ──
ezd.GotoPos(100, 100); // 振镜移动到指定坐标
ezd.GetCurCoor(out double x, out double y);
ezd.SetAxisMoveParam(0, 0.5, 1, 50); // 设置扩展轴参数
ezd.AxisMoveTo(0, 120); // 扩展轴移动到指定位置
// ── 飞行标刻 ──
double speed = 0;
ezd.GetFlySpeed(ref speed);
int ret = await ezd.MarkEntityFlyAsync("Text1");
// ── 错误处理 ──
if (!EzdErrorCode.IsSuccess(ret))
Console.WriteLine(EzdErrorCode.GetMessage(ret)); // 中文错误信息
发布
dotnet pack -c Release
dotnet nuget push bin\Release\QianTools.*.nupkg --source https://api.nuget.org/v3/index.json
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0-windows7.0 is compatible. net7.0-windows was computed. net8.0-windows was computed. net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. |
-
net6.0-windows7.0
- ClosedXML (>= 0.104.2)
- Microsoft.Data.Sqlite (>= 10.0.10)
- Newtonsoft.Json (>= 13.0.4)
- SerialPortStream (>= 2.4.2)
- System.Diagnostics.PerformanceCounter (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
- System.Management (>= 8.0.0)
-
net8.0-windows7.0
- ClosedXML (>= 0.104.2)
- Microsoft.Data.Sqlite (>= 10.0.10)
- Newtonsoft.Json (>= 13.0.4)
- SerialPortStream (>= 2.4.2)
- System.Diagnostics.PerformanceCounter (>= 8.0.0)
- System.IO.Ports (>= 8.0.0)
- System.Management (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
2.5.0:EtherNetIPClient / Ex1121ModbusClient 优化 — 线程安全(Interlocked)、异步断连(DisconnectAsync)、DI 位解析(ReadDiBitsAsync/GetDiBits)、DO 看门狗(StartWatchdog)、ILogger 日志接口、CIP 状态码中文翻译。