using System.Text;
|
using GSModbus.Config;
|
|
namespace GSModbus.Config
|
{
|
/// <summary>
|
/// Modbus数据解析器 - 根据配置将原始寄存器数据解析为具体类型
|
/// </summary>
|
public static class ModbusDataParser
|
{
|
#region 主要解析方法
|
|
/// <summary>
|
/// 根据字段配置解析寄存器数据
|
/// </summary>
|
/// <param name="registers">原始寄存器数据</param>
|
/// <param name="fieldConfig">字段配置</param>
|
/// <returns>解析后的数据对象</returns>
|
public static object? ParseFieldData(int[] registers, DataField fieldConfig)
|
{
|
if (registers == null || registers.Length == 0)
|
{
|
return GetDefaultValue(fieldConfig.DataType);
|
}
|
|
try
|
{
|
return fieldConfig.DataType.ToLower() switch
|
{
|
"byte" => ParseByte(registers, fieldConfig),
|
"integer" => ParseInteger(registers, fieldConfig),
|
"string" => ParseString(registers, fieldConfig),
|
"timestamp" => ParseTimestamp(registers, fieldConfig),
|
"float" => ParseFloat(registers, fieldConfig),
|
"boolean" => ParseBoolean(registers, fieldConfig),
|
_ => throw new NotSupportedException($"不支持的数据类型: {fieldConfig.DataType}")
|
};
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"[PARSER-ERROR] 解析字段 '{fieldConfig.DisplayName}' 时发生错误: {ex.Message}");
|
return GetDefaultValue(fieldConfig.DataType);
|
}
|
}
|
|
#endregion
|
|
#region 具体类型解析方法
|
|
/// <summary>
|
/// 解析字节数据
|
/// </summary>
|
private static byte ParseByte(int[] registers, DataField config)
|
{
|
if (registers.Length == 0) return 0;
|
|
// 取第一个寄存器的低字节
|
var value = registers[0] & 0xFF;
|
return (byte)value;
|
}
|
|
/// <summary>
|
/// 解析整数数据(支持多寄存器和缩放)
|
/// </summary>
|
private static object ParseInteger(int[] registers, DataField config)
|
{
|
if (registers.Length == 0) return 0;
|
|
long value = 0;
|
|
if (config.Length == 1)
|
{
|
// 单寄存器
|
value = registers[0];
|
}
|
else if (config.Length == 2)
|
{
|
// 双寄存器,根据字节序组合
|
if (config.Encoding?.ToLower() == "littleendian")
|
{
|
// 小端:低位在前
|
value = registers[0] | (registers[1] << 16);
|
}
|
else
|
{
|
// 大端:高位在前(默认)
|
value = (registers[0] << 16) | registers[1];
|
}
|
}
|
else
|
{
|
// 多寄存器:按大端顺序组合
|
for (int i = 0; i < Math.Min(registers.Length, config.Length); i++)
|
{
|
value = (value << 16) | (registers[i] & 0xFFFF);
|
}
|
}
|
|
// 应用缩放因子
|
if (config.Scale != 1.0 && config.Scale != 0.0)
|
{
|
var scaledValue = value * config.Scale;
|
return Math.Round(scaledValue, config.DecimalPlaces);
|
}
|
|
return value;
|
}
|
|
/// <summary>
|
/// 解析字符串数据
|
/// </summary>
|
private static string ParseString(int[] registers, DataField config)
|
{
|
if (registers.Length == 0) return string.Empty;
|
|
var bytes = new List<byte>();
|
|
foreach (var register in registers.Take(config.Length))
|
{
|
if (config.Encoding?.ToLower() == "littleendian")
|
{
|
// 小端:低字节在前
|
bytes.Add((byte)(register & 0xFF));
|
bytes.Add((byte)((register >> 8) & 0xFF));
|
}
|
else
|
{
|
// 大端:高字节在前(默认)
|
bytes.Add((byte)((register >> 8) & 0xFF));
|
bytes.Add((byte)(register & 0xFF));
|
}
|
}
|
|
// 移除尾部的空字符
|
while (bytes.Count > 0 && bytes[bytes.Count - 1] == 0)
|
{
|
bytes.RemoveAt(bytes.Count - 1);
|
}
|
|
return Encoding.ASCII.GetString(bytes.ToArray());
|
}
|
|
/// <summary>
|
/// 解析时间戳数据
|
/// </summary>
|
private static DateTime ParseTimestamp(int[] registers, DataField config)
|
{
|
if (registers.Length == 0) return DateTime.MinValue;
|
|
try
|
{
|
// 先解析为字符串
|
var timestampString = ParseString(registers, config);
|
|
if (string.IsNullOrEmpty(timestampString))
|
{
|
return DateTime.MinValue;
|
}
|
|
// 根据格式解析时间戳
|
var format = config.Format ?? "YYYYMMDDHHMMSS";
|
|
return format.ToUpper() switch
|
{
|
"YYYYMMDDHHMMSS" => ParseTimestampYYYYMMDDHHMMSS(timestampString),
|
"YYYYMMDDHHMM" => ParseTimestampYYYYMMDDHHMM(timestampString),
|
"YYMMDDHHMMSS" => ParseTimestampYYMMDDHHMMSS(timestampString),
|
_ => DateTime.TryParse(timestampString, out var dt) ? dt : DateTime.MinValue
|
};
|
}
|
catch (Exception ex)
|
{
|
Console.WriteLine($"[PARSER-ERROR] 解析时间戳失败: {ex.Message}");
|
return DateTime.MinValue;
|
}
|
}
|
|
/// <summary>
|
/// 解析浮点数数据
|
/// </summary>
|
private static float ParseFloat(int[] registers, DataField config)
|
{
|
if (registers.Length < 2) return 0.0f;
|
|
// IEEE 754单精度浮点数需要2个寄存器(32位)
|
uint value;
|
|
if (config.Encoding?.ToLower() == "littleendian")
|
{
|
value = (uint)(registers[0] | (registers[1] << 16));
|
}
|
else
|
{
|
value = (uint)((registers[0] << 16) | registers[1]);
|
}
|
|
// 将32位整数转换为浮点数
|
return BitConverter.ToSingle(BitConverter.GetBytes(value), 0);
|
}
|
|
/// <summary>
|
/// 解析布尔数据
|
/// </summary>
|
private static bool ParseBoolean(int[] registers, DataField config)
|
{
|
if (registers.Length == 0) return false;
|
|
// 非零值视为true
|
return registers[0] != 0;
|
}
|
|
#endregion
|
|
#region 时间戳解析辅助方法
|
|
/// <summary>
|
/// 解析YYYYMMDDHHMMSS格式的时间戳
|
/// </summary>
|
private static DateTime ParseTimestampYYYYMMDDHHMMSS(string timestampString)
|
{
|
if (timestampString.Length < 14) return DateTime.MinValue;
|
|
var year = int.Parse(timestampString.Substring(0, 4));
|
var month = int.Parse(timestampString.Substring(4, 2));
|
var day = int.Parse(timestampString.Substring(6, 2));
|
var hour = int.Parse(timestampString.Substring(8, 2));
|
var minute = int.Parse(timestampString.Substring(10, 2));
|
var second = int.Parse(timestampString.Substring(12, 2));
|
|
return new DateTime(year, month, day, hour, minute, second);
|
}
|
|
/// <summary>
|
/// 解析YYYYMMDDHHMM格式的时间戳
|
/// </summary>
|
private static DateTime ParseTimestampYYYYMMDDHHMM(string timestampString)
|
{
|
if (timestampString.Length < 12) return DateTime.MinValue;
|
|
var year = int.Parse(timestampString.Substring(0, 4));
|
var month = int.Parse(timestampString.Substring(4, 2));
|
var day = int.Parse(timestampString.Substring(6, 2));
|
var hour = int.Parse(timestampString.Substring(8, 2));
|
var minute = int.Parse(timestampString.Substring(10, 2));
|
|
return new DateTime(year, month, day, hour, minute, 0);
|
}
|
|
/// <summary>
|
/// 解析YYMMDDHHMMSS格式的时间戳
|
/// </summary>
|
private static DateTime ParseTimestampYYMMDDHHMMSS(string timestampString)
|
{
|
if (timestampString.Length < 12) return DateTime.MinValue;
|
|
var year = 2000 + int.Parse(timestampString.Substring(0, 2)); // 假设是21世纪
|
var month = int.Parse(timestampString.Substring(2, 2));
|
var day = int.Parse(timestampString.Substring(4, 2));
|
var hour = int.Parse(timestampString.Substring(6, 2));
|
var minute = int.Parse(timestampString.Substring(8, 2));
|
var second = int.Parse(timestampString.Substring(10, 2));
|
|
return new DateTime(year, month, day, hour, minute, second);
|
}
|
|
#endregion
|
|
#region 工具方法
|
|
/// <summary>
|
/// 获取数据类型的默认值
|
/// </summary>
|
/// <param name="dataType">数据类型字符串</param>
|
/// <returns>默认值对象</returns>
|
private static object? GetDefaultValue(string dataType)
|
{
|
return dataType.ToLower() switch
|
{
|
"byte" => (byte)0,
|
"integer" => 0,
|
"string" => string.Empty,
|
"timestamp" => DateTime.MinValue,
|
"float" => 0.0f,
|
"boolean" => false,
|
_ => null
|
};
|
}
|
|
/// <summary>
|
/// 格式化显示值(应用值映射和单位)
|
/// </summary>
|
/// <param name="value">原始值</param>
|
/// <param name="config">字段配置</param>
|
/// <returns>格式化后的显示字符串</returns>
|
public static string FormatDisplayValue(object? value, DataField config)
|
{
|
if (value == null)
|
{
|
return "N/A";
|
}
|
|
// 检查是否有值映射
|
if (config.ValueMap != null && config.ValueMap.Count > 0)
|
{
|
var key = value.ToString() ?? "";
|
if (config.ValueMap.TryGetValue(key, out var mappedValue))
|
{
|
return mappedValue;
|
}
|
}
|
|
// 格式化数值类型
|
var formattedValue = value switch
|
{
|
double d => d.ToString($"F{config.DecimalPlaces}"),
|
float f => f.ToString($"F{config.DecimalPlaces}"),
|
DateTime dt => dt.ToString("yyyy-MM-dd HH:mm:ss"),
|
_ => value.ToString()
|
};
|
|
// 添加单位
|
if (!string.IsNullOrEmpty(config.Unit))
|
{
|
formattedValue += $" {config.Unit}";
|
}
|
|
return formattedValue ?? "";
|
}
|
|
/// <summary>
|
/// 验证寄存器数据长度是否符合配置要求
|
/// </summary>
|
/// <param name="registers">寄存器数据</param>
|
/// <param name="config">字段配置</param>
|
/// <returns>验证结果</returns>
|
public static bool ValidateRegisterLength(int[] registers, DataField config)
|
{
|
return registers != null && registers.Length >= config.Length;
|
}
|
|
#endregion
|
}
|
}
|