quowingwang 517b3a79d3 安灯
2025-12-24 14:26:46 +08:00

256 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DOAN.ServiceCore.MyMatchPush
{
public class Watchup
{
// 修复线程安全的包序号Interlocked确保多线程不重复比自增更可靠
private static int _packageSort = 1;
public static bool StartPush(string message, SocketGatewayServer _socketGateway, string watchLocalAddress)
{
// 仅注册一次GBK编码避免重复注册报错
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding GBKEncoding = Encoding.GetEncoding(936);
// 前置校验:网关实例/消息不能为空
if (_socketGateway == null)
{
Console.WriteLine("错误:网关实例不能为空");
return false;
}
if (string.IsNullOrEmpty(message))
{
Console.WriteLine("错误:发送消息不能为空");
return false;
}
try
{
// 1. 包头固定0x5A
byte[] packageHeader = { 0x5A };
// 2. 包序号:线程安全自增+1-255循环修复核心问题
byte packSort = (byte)Interlocked.Increment(ref _packageSort);
if (_packageSort > 255)
{
Interlocked.Exchange(ref _packageSort, 1);
packSort = 1;
}
byte[] packsort = { packSort };
// 3. 地址:广播地址(可替换为手表主机地址/本机地址)
//byte[] IPaddress = { 0xff, 0xff, 0xff, 0xff };
// 3. 核心修改:根据传入的本机地址生成目标地址字节数组(替换原广播地址)
byte[] IPaddress;
if (string.IsNullOrEmpty(watchLocalAddress))
{
// 空地址则用广播地址(兼容原有逻辑)
IPaddress = new byte[] { 0xff, 0xff, 0xff, 0xff };
Console.WriteLine("未指定手表地址,使用广播模式");
}
else
{
// 将手表本机地址如012-034-056-078转换为4字节数组
try
{
IPaddress = ConvertWatchAddressToBytes(watchLocalAddress);
Console.WriteLine($"目标手表地址转换完成:{watchLocalAddress} → {BitConverter.ToString(IPaddress).Replace("-", " ")}");
}
catch (Exception ex)
{
Console.WriteLine($"地址转换失败:{ex.Message}");
return false;
}
}
// 4. 命令码0xE0显示文字
byte[] CommandCode = { 0xE0 };
//5. 数据段GBK编码+限制84字节修复超长问题
byte[] MessageData = GBKEncoding.GetBytes(message);
const int MaxDataLength = 84;
if (MessageData.Length > MaxDataLength)
{
// 反向查找确保截断位置不是中文的第二个字节GBK中文占2字节
MessageData = SafeTruncateGBKBytes(MessageData, MaxDataLength);
Console.WriteLine($"[警告] 消息超长,已安全截断为{MessageData.Length}字节");
}
byte messageLength = (byte)MessageData.Length;
if (messageLength != MessageData.Length)
{
Console.WriteLine("[错误] 消息长度超出byte范围");
return false;
}
byte[] MessageLength = { messageLength };
// 6. 拼接除CRC外的所有数据
byte[] dataWithoutCRC = packageHeader
.Concat(packsort)
.Concat(IPaddress)
.Concat(CommandCode)
.Concat(MessageLength)
.Concat(MessageData).ToArray();
// 7. 校验和:内置正确实现(符合手表协议:所有字节之和%256
byte crc = CalculateChecksum(dataWithoutCRC);
byte[] CRC = { crc };
// 8. 最终手表指令包
byte[] body = dataWithoutCRC.Concat(CRC).ToArray();
// 调试输出:确认手表指令包格式
string hexString = BitConverter.ToString(body).Replace("-", " ");
Console.WriteLine($"生成手表指令包:{hexString}");
// 9. 关键修复:按网关协议封装后再发送(核心改进处)
byte[] gatewayPackage = PackToGatewayProtocol(body);
// 调试输出:封装后的网关包(便于核对协议格式)
string gatewayHex = BitConverter.ToString(gatewayPackage).Replace("-", " ");
Console.WriteLine($"封装后的网关协议包:{gatewayHex}");
return _socketGateway.SendToGateway(gatewayPackage);
}
catch (Exception ex)
{
Console.WriteLine($"指令包生成/发送失败:{ex.Message}");
return false;
}
}
/// <summary>
/// 将手表本机地址格式XXX-XXX-XXX-XXX转换为4字节数组
/// 示例012-034-056-078 → [0x0C, 0x22, 0x38, 0x4E]12→0C34→2256→3878→4E
/// </summary>
/// <param name="addressStr">手表本机地址如012-034-056-078</param>
/// <returns>4字节地址数组</returns>
private static byte[] ConvertWatchAddressToBytes(string addressStr)
{
// 校验地址格式
string[] segments = addressStr.Split('-');
if (segments.Length != 4)
{
throw new ArgumentException($"地址格式错误需为XXX-XXX-XXX-XXX当前{addressStr}");
}
byte[] addressBytes = new byte[4];
for (int i = 0; i < 4; i++)
{
// 转换每段为数字并校验范围0-255
if (!int.TryParse(segments[i], out int segmentValue) || segmentValue < 0 || segmentValue > 255)
{
throw new ArgumentException($"地址段{segments[i]}无效需为0-255之间的整数");
}
addressBytes[i] = (byte)segmentValue;
}
return addressBytes;
}
/// <summary>
/// 内置校验和计算(符合手表协议:所有字节之和%256
/// </summary>
private static byte CalculateChecksum(byte[] data)
{
if (data == null || data.Length == 0) return 0;
long sum = 0;
foreach (byte b in data) sum += b;
return (byte)(sum % 256);
}
/// <summary>
/// 关键改进严格按网关协议封装适配LoRa转发指令
/// 修复点网关MAC替换为实际值、校验和计算规则、字段顺序验证
/// </summary>
private static byte[] PackToGatewayProtocol(byte[] watchPackage)
{
// 网关协议格式(严格对照产品手册):
// 68(帧头) + 网关MAC(6字节) + 命令类型(1字节) + 命令码(1字节) + 数据长度(2字节) + 手表指令包 + 校验和(2字节) + 16(帧尾)
// 改进1替换为网关实际MAC从网关返回包中提取30 9C 23 DA 8B 00
byte[] frameHeader = { 0x68 }; // 网关帧头(固定)
byte[] gatewayMac = { 0x30, 0x9C, 0x23, 0xDA, 0x8B, 0x00 }; // 核心修改改为网关实际MAC之前是示例值
byte[] cmdType = { 0xE4 }; // 网关命令类型固定LoRa数据转发
byte[] cmdCode = { 0xA1 }; // 网关命令码(固定:转发手表数据)
// 改进2数据长度严格按协议要求高位在前确保网关识别
ushort dataLengthValue = (ushort)watchPackage.Length;
byte[] dataLen = new byte[2];
dataLen[0] = (byte)(dataLengthValue >> 8); // 高位字节
dataLen[1] = (byte)(dataLengthValue & 0xFF); // 低位字节
// 拼接网关包(除校验和、帧尾)
byte[] gatewayDataWithoutCrc = frameHeader
.Concat(gatewayMac)
.Concat(cmdType)
.Concat(cmdCode)
.Concat(dataLen)
.Concat(watchPackage)
.ToArray();
// 改进3网关校验和严格按协议求和取低位2字节手动计算避免大小端问题
ushort gatewayCrc = (ushort)(gatewayDataWithoutCrc.Sum(b => b) % 65536);
byte[] gatewayCrcBytes = new byte[2];
gatewayCrcBytes[0] = (byte)(gatewayCrc >> 8); // 校验和高位
gatewayCrcBytes[1] = (byte)(gatewayCrc & 0xFF); // 校验和低位
// 拼接最终网关包
byte[] frameTail = { 0x16 }; // 网关帧尾(固定)
return gatewayDataWithoutCrc
.Concat(gatewayCrcBytes)
.Concat(frameTail)
.ToArray();
}
/// <summary>
/// 安全截断GBK字节数组避免截断到中文半个字符
/// </summary>
/// <param name="bytes">原始GBK字节数组</param>
/// <param name="maxLength">最大长度</param>
/// <returns>截断后的字节数组</returns>
private static byte[] SafeTruncateGBKBytes(byte[] bytes, int maxLength)
{
if (bytes == null || maxLength <= 0) return Array.Empty<byte>();
if (bytes.Length <= maxLength) return bytes;
// GBK编码规则
// - 单字节0x00-0x7F字母/数字/符号)
// - 双字节0x81-0xFE 开头,第二个字节 0x40-0xFE
int truncateLength = maxLength;
// 检查截断位置的前一个字节是否是中文的第二个字节
while (truncateLength > 0)
{
byte b = bytes[truncateLength - 1];
// 如果是双字节的第二个字节,向前退一位
if (b >= 0x40 && b <= 0xFE && truncateLength > 1)
{
byte prevB = bytes[truncateLength - 2];
if (prevB >= 0x81 && prevB <= 0xFE)
{
truncateLength--;
continue;
}
}
break;
}
byte[] result = new byte[truncateLength];
Array.Copy(bytes, result, truncateLength);
return result;
}
}
}