中文 | English
QLProtocolLibrary 是一个面向 QL 设备主应用层协议的 C# 类库。
当前实现对齐的主报文结构为:
设备地址(4) + 功能码(1) + 功能数据(N) + CRC16(2)
同时支持文档中的可选包络:
C6 F4 C2 CC + 长度(2) + 裸报文 + 0D 0A
它的目标很直接:
- 让调用方直接组协议报文
- 让调用方直接解协议报文
- 上位机直接通过串口、TCP、RS485 发送和接收报文
- 设备调试工具、协议测试工具
- 需要把协议能力封装成公共库的项目
- 外部项目通过 NuGet 复用协议构包和解包能力
dotnet add package QLProtocolLibraryusing QLProtocolLibrary;
uint deviceAddress = 0x10000001;
byte[] requestBytes = QlProtocolCommandBuilder.BuildRead(deviceAddress, 0x0000, 0x0001);
Console.WriteLine(QlHexConverter.ToHexString(requestBytes));
// 10 00 00 01 03 00 00 00 01 43 210x03:读寄存器0x06:写寄存器0x08:操作指令0x23:读日志0x26:写日志0x30:TF 目录/文件读取0x32:指令转发0x33:数据库读取
using QLProtocolLibrary;
uint deviceAddress = 0x10000001;
byte[] requestBytes = QlProtocolCommandBuilder.BuildRead(deviceAddress, 0x0000, 0x0001);
Console.WriteLine(QlHexConverter.ToHexString(requestBytes));
// 10 00 00 01 03 00 00 00 01 43 21
byte[] responseBytes =
{
0x10, 0x00, 0x00, 0x01,
0x03,
0x00, 0x00,
0x04,
0x1C, 0x04, 0x1F, 0x41,
0x97, 0xE9
};
QlProtocolFrame frame = QlProtocolParser.Parse(responseBytes);
float value = frame.ReadSingle();
Console.WriteLine(value); // 9.9385using QLProtocolLibrary;
uint deviceAddress = 0x10000005;
byte[] requestBytes = QlProtocolCommandBuilder.BuildWriteFloat(deviceAddress, 0x164E, 0.0596f);
Console.WriteLine(QlHexConverter.ToHexString(requestBytes));
// 10 00 00 05 06 16 4E 00 01 04 21 1F 74 3D 05 E4
byte[] responseBytes =
{
0x10, 0x00, 0x00, 0x05,
0x06,
0x16, 0x4E,
0x01,
0x60,
0x2A, 0x82
};
QlProtocolFrame frame = QlProtocolParser.Parse(responseBytes);
Console.WriteLine(frame.Kind); // WriteResponse
Console.WriteLine($"0x{frame.ResponseCode.GetValueOrDefault():X2}"); // 0x600x32 的当前约定为:
设备地址(4) + 0x32(1) + 数据长度(2) + 端口ID(1) + 转发内容(N) + CRC16(2)
其中:
- 数据长度 =
端口ID + 转发内容的总字节数 CRC16仍然按低字节在前发送- 转发内容本身通常就是另一条完整的正常协议命令
从 0.5.0 开始,可以直接用正式 API:
using QLProtocolLibrary;
byte[] forwardedCommand = QlHexConverter.FromHexString(
"10 00 00 01 06 00 16 00 01 04 01 00 00 00 2E F9");
byte[] forwardFrame = QlProtocolCommandBuilder.BuildForward(
0x1000000F,
0x01,
forwardedCommand);
Console.WriteLine(QlHexConverter.ToHexString(forwardFrame));
// 10 00 00 0F 32 00 11 01 10 00 00 01 06 00 16 00 01 04 01 00 00 00 2E F9 CE D2
QlProtocolFrame parsed = QlProtocolParser.Parse(forwardFrame);
Console.WriteLine(parsed.ReadForwardPortId()); // 1
Console.WriteLine(QlHexConverter.ToHexString(parsed.ReadForwardContent()));
// 10 00 00 01 06 00 16 00 01 04 01 00 00 00 2E F9这个库分两层:
适合已经有明确寄存器定义的业务调用。
例如:
QlProtocolKnownCommands.BuildReadDeviceTime(deviceAddress)QlProtocolKnownCommands.BuildReadRunStatus(deviceAddress)QlKnownOperations.DeviceTime.BuildRead(deviceAddress)QlProtocolKnownParsers.TryParseRunStatus(frame, out var status)
适合调试协议、扩展自定义地址、处理原始报文。
例如:
QlProtocolCommandBuilder.BuildRead(...)QlProtocolCommandBuilder.BuildWriteFloat(...)QlProtocolCommandBuilder.BuildForward(...)QlProtocolParser.Parse(...)frame.ReadSingle()frame.ReadForwardPortId()
常用方法:
BuildPacket(uint deviceAddress, byte rawFunctionCode, byte[] functionData, bool includeEnvelope = false)BuildRead(uint deviceAddress, ushort address, ushort registerCount, bool includeEnvelope = false)BuildWrite(uint deviceAddress, ushort address, ushort registerCount, byte[] payload, bool includeEnvelope = false)BuildWriteRegisters(uint deviceAddress, ushort address, params ushort[] registers)BuildWriteFloat(uint deviceAddress, ushort address, params float[] values)BuildWriteUtf8(uint deviceAddress, ushort address, string value, int fixedByteLength = 0)BuildSetTime(uint deviceAddress, DateTime value)BuildForward(uint deviceAddress, byte portId, byte[] forwardedContent, bool includeEnvelope = false)
常用方法:
Parse(byte[] frameBytes)ParseHex(string hex)TryParse(byte[] frameBytes, out QlProtocolFrame? frame)TryParseHex(string hex, out QlProtocolFrame? frame)
常用方法:
ReadUInt16()ReadUInt32()ReadSingle()ReadSingles()ReadUtf8()ReadAscii()ReadBcdDateTime()ReadBcdDateTimeText()ReadUInt16Array()ReadForwardPortId()ReadForwardContent()
QlProtocolKnownCommandsQlProtocolKnownParsersQlKnownOperationsQlProtocolKnownRouter
这份协议不能简单理解成“所有字段都同一种字节序”。
建议这样理解:
- 协议控制字段 例如寄存器地址、寄存器数量、长度字段 按文档约定使用高字节在前
- 数据值字段
例如
WORD、FLOAT、日志内容、操作参数 必须按具体功能码章节和数据类型定义处理
当前库中的辅助方法拆分为:
EncodeUInt16 / DecodeUInt16用于协议控制字段EncodeValueUInt16 / DecodeValueUInt16用于 16 位数据值EncodeUInt32 / DecodeUInt32用于 32 位数据值EncodeSingle / DecodeSingle用于浮点值
0x03 / 0x06已经具备明确的构包和解包支持0x32已经具备正式的构包 helper 和解析 helper0x08 / 0x23 / 0x26 / 0x30 / 0x33当前仍以通用结构解析为主- 更细的业务字段解释,仍建议按项目实际文档章节继续扩展
src/QLProtocolLibrary:类库源码examples/QLProtocolLibrary.Demo:源码引用示例examples/QLProtocolLibrary.NuGetDemo:NuGet 使用示例tests/QLProtocolLibrary.Tests:单元测试docs:API 文档与发布文档
- NuGet 包 README:src/QLProtocolLibrary/README.md
- API 参考(中文):docs/API.zh-CN.md
- API Reference (English):docs/API.en.md
- 发布清单(中文):docs/PUBLISHING.zh-CN.md
- Publishing checklist (English):docs/PUBLISHING.en.md
- 版本变更:CHANGELOG.md
- GitHub 仓库:https://github.com/zpczpc/QLProtocolLibrary
- Issue 反馈:https://github.com/zpczpc/QLProtocolLibrary/issues
- NuGet 包:https://www.nuget.org/packages/QLProtocolLibrary
- 许可证:MIT,见 LICENSE