-3

在过去的 2 年里,我一直在问这个问题,并且仍在寻找一种好的方法来做到这一点。我正在做的事情如下:

我有一个过去 3 年开发的 WPF/C# 应用程序。它通过 UDP 端口获取实时字节流。每个记录集为 1000 字节。我每秒获得 100 个这些字节记录。我正在读取数据并对其进行处理,以便以各种格式显示。这些逻辑记录是子交换的。

前 300 个字节相同,每个逻辑记录包含 Byte、Int16、UInt16、Int32 和 UInt32 值的混合。这些值中的大约 70% 最终乘以最低有效位以创建 Double。这些参数始终相同。第二个 300 字节是 Byte、Int16、UIn32、Int32 和 UInt32 值的另一种混合。再次将这些值中的大约 70% 乘以 LSB 以创建 Double。这些参数又总是相同的。最后一段为 400 字节并进行了子交换。这意味着记录的最后一部分包含 20 种不同逻辑记录格式中的 1 种。我称它们为 Type01...Type20 数据。有一个标识符字节告诉我它是哪一个。这些再次包含需要转换的 Byte、Int、UInt 数据值。

我目前正在使用数百个函数调用来处理这些数据。每个函数调用都将 1000 字节数组作为参数,一个偏移量(索引)到参数开始的字节数组中。然后它使用 BitConverter.ToXXX 调用将字节转换为正确的数据类型,然后在必要时乘以 LSB 以创建最终数据值并将其返回。

我正在尝试简化此处理,因为数据流会根据源而变化。例如,其中一个新数据源(提要)在前 300 个字节中更改了大约 20 个参数,在第二个 300 个字节中更改了大约 24 个参数,在最后一个子交换的 400 字节记录中更改了几个参数。

我想构建一个数据字典,其中字典包含逻辑记录号(数据类型)、记录偏移量、数据 LSB、要转换为的数据类型(Int16、UInt32 等),最后输出类型( Int32、Double 等)。也许还包括要使用的 BitConverter 函数和“动态转换”?

这似乎是使用模板类和可能的委托的练习,但我不知道如何做到这一点。我将不胜感激一些代码,例如示例。

数据也被记录下来,因此回放可以以 2x、4x、8x、16x 的速度运行。现在,在有人评论如何以这些速度查看数千个参数之前,这并不像人们想象的那么难。某些类型的数据,例如绿色背景为好,红色为坏;或随着时间的推移绘制地图位置 (LAT/LON) 非常适合快速回放以查找有趣的事件。所以这是可能的。

提前感谢您的帮助。

我不确定其他人是否知道我正在尝试做什么,所以我想我会发布一小段源代码,看看是否有人可以改进它。

就像我上面说的,数据以字节流的形式出现。在字节数组中读取它后,它将如下所示:

Byte[] InputBuffer = { 0x01, 0x00, 0x4F, 0xEB, 0x06, 0x00, 0x17, 0x00, 
0x00, 0x00, ...    };

前 2 个字节是一个等于 1 的 ushort。这是此特定记录的记录类型。此数字的范围可以从 1 到 20。

接下来的 4 个字节是一个 uint,等于 453,455。该值是十分之一秒的数量。本例中的值为 12:35:45.5。为此,我将对以下子例程进行以下调用:

labelTimeDisplay.Content = TimeField(InputBuffer, 2, .1).ToString();

public Double TimeField(Byte[] InputBuffer, Int32 Offset, Double lsb)
{
   return BitConverter.ToUInt32(InputBuffer, Offset) * lsb;
}

下一个数据字段是软件版本,在本例中为 23

labelSoftwareVersion.Content = SoftwareVersion(InputBuffer, 6).ToString();

public UInt16 SoftwareVersion(Byte[] InputBuffer, Int32 Offset)
{
   return BitConverter.ToUInt16(InputBuffer, Offset);
}

下一个数据字段是另一个 UInt16 的系统状态字。

如果 16 位中的任何一个设置为逻辑 1,则内置测试状态位将传递给其他例程。

UInt16 CheckStatus = SystemStatus(InputBuffer, 8);

public UInt16 SystemStatus(Byte[] InputBuffer, Int32 Offset)
{
   return BitConverter.ToUInt16(InputBuffer, Offset);
}

我实际上有超过一千个单独的子程序来处理存储在字节数组中的数据。字节数组始终为 1000 字节的固定长度。前 6 个字节总是相同的,标识符和时间。之后,每一帧的参数都不同。

我对软件进行了一些重大修改,这些修改将为下一个软件版本重新定义许多参数。我仍然必须支持旧的软件版本,所以软件变得更加复杂。我的目标是找到一种使用字典查找来处理数据的方法。这样我就可以创建字典并阅读字典以了解如何处理数据。也许使用循环将数据加载到集合中,然后将其绑定到显示字段。

像这样的东西:

public class ParameterDefinition
{
    String ParameterNumber;
    String ParameterName;
    Int32 Offset;
    Double lsb;
    Type ReturnDataType;
    Type BaseDataType;
}

private ParameterDefinition[] parms = new ParameterDefinition[]
{
   new ParameterDefinition ( "0000","RecordID",  0, 0.0,  typeof(UInt16), typeof(UInt16)), 
   new ParameterDefinition ( "0001",    "Time",  2, 0.1,  typeof(Double), typeof(UInt32)), 
   new ParameterDefinition ( "0002",   "SW ID",  6, 0.0,  typeof(UInt16), typeof(UInt16)),
   new ParameterDefinition ( "0003",  "Status",  8, 0.0,  typeof(UInt16), typeof(UInt16)),  
   // Lots more parameters
}

我的底线问题是让参数定义强制转换或选择正确的函数。我找不到将“字典”链接到实际数据输出的方法

谢谢你的帮助

4

2 回答 2

1

使用数据字典来表示数据结构很好,只要您不为每个单独的记录遍历字典。相反,使用反射发射或表达式树来构建一个可以多次调用的委托。

于 2015-07-22T20:58:09.547 回答
0

听起来您正在手动反序列化字节流,其中字节代表各种数据类型。这个问题之前已经解决了。

尝试定义一个代表前 600 个字节的类,并使用Protocol Buffer Serializer对其进行反序列化和反序列化(该实现由 SO 自己的 Marc Gravell实现,而顶级 SO 贡献者 Jon Skeet则提供了不同的实现)。

协议缓冲区是一种语言中立、平台中立、可扩展的结构化数据序列化方式,用于通信协议和数据存储。您只需定义一次数据的结构化方式,然后就可以使用特殊生成的源代码轻松地将结构化数据写入和读取各种数据流,并使用各种语言。您甚至可以在不破坏针对“旧”格式编译的已部署程序的情况下更新您的数据结构。

来源,以及我没有亲自使用过的第三个实现。

对于最后 300 字节,为适当的格式创建适当的类定义,并再次使用协议缓冲区来反序列化适当的类。

对于最后的修饰(例如将值转换为双精度值),您可以对类进行后处理,或者只使用一个返回适当最终数字的 getter。

于 2015-07-22T21:00:57.650 回答