当前位置: 首页 > 编程语言 > C#

串口数据共享二进制通信协议解析器设计

时间:2023-04-10 21:04:23 C#

串口数据字节二进制通信协议解析器设计)。数据包结构(无法更改)为:||开始定界符(1字节)|消息ID(1字节)|长度(1字节)|有效负载(n字节)|校验和(1字节)||状态机方法实现了这样一个系统。当数据的每个字节到达时,状态机被驱动以一次一个/字节地查看传入数据是否适合有效数据包,并且一旦整个数据包组装完毕,基于消息ID的switch语句将执行适当的消息处理程序。在某些实现中,解析器/状态机/消息处理程序循环在其自己的线程中,以免给事件处理程序增加串行??数据接收的负担,并由指示已读取字节的信号量触发。我想知道是否有更优雅的解决方案来解决这个常见问题,利用C#和OO设计的一些更现代的语言功能。有什么设计模式可以解决这个问题?事件驱动vs轮询vs组合?我很想听听你的想法。谢谢。Prembo。首先,我将数据包解析器与流读取器分开(因此我可以在不处理流的情况下编写测试)。然后考虑一个基类,它提供读取数据包的方法和写入数据包的方法。另外,我会像这样构建一个字典(一次只用于将来的调用):classProgram{staticvoidMain(string[]args){IDictionary>messages=assembly.GetTypes().Where(t=>typeof(Message).IsAssignableFrom(t)&&!t.IsAbstract).Select(t=>new{Keys=t.GetCustomAttributes(typeof(AcceptsAttribute),true).Cast().Select(attr=>attr.MessageId),Value=(Func)Expression.Lambda(Expression.Convert(Expression.New(t),typeof(Message))).Compile()}).SelectMany(o=>o.Keys.Select(key=>new{Key=key,o.Value})).ToDictionary(o=>o.Key,v=>v.Value);//如果多个类接受相同的消息id,则在创建时会给你一个运行时错误,//AcceptsAttribute)表示FooMessage类接受5的消息ID。第二:是的,字典是在运行时通过反射构建的。你只需要这样做一次(我会把它放到一个单例类中,你可以在它上面放一个你可以运行的测试用例来确保字典构建正确)。第三:varm=messages[5]();此行从字典中获取以下已编译的lambda表达式并执行它:()=>(Message)newFooMessage();(由于委托工作方式的协变变化,必须在.NET3.5中使用强制转换,但在4.0中,可以将Func类型的对象分配给Func类型的对象。)此lambda表达式是从值分配行构建的dictionarycreation:Value=(Func)Expression.Lambda(Expression.Convert(Expression.New(t),typeof(Message))).Compile()(这里的cast是将编译后的lambda表达式Func转换为Func的必要条件.)我就是这样做的,因为我碰巧已经有了那种类型。您还可以使用:Value=()=>(Message)Activator.CreateInstance(t)但我相信这会更慢(并且需要在此处将Func更改为Func)。第四:.SelectMany(o=>o.Keys.Select(key=>new{Key=key,o.Value}))这样做是因为我认为你可能需要在一个类上多次放置AcceptsAttribute(每个类接受多个消息ID)。这还有一个很好的副作用,即忽略没有消息id属性的消息类(否则Where方法需要具有确定属性是否存在的复杂性)。我来晚了一点,但我写了一个我认为可以做到这一点的框架。我很难在不知道你的协议的情况下编写对象模型,但我认为这不会太难。查看binaryserializer.com。我通常做的是定义一个抽象的基本消息类并从该类派生密封消息。然后是一个消息解析器对象,它包含一个状态机来解释字节并构建适当的消息对象。消息解析器对象只有一个方法(传递传入字节)和可选事件(在完整消息到达时调用)。然后您有两个选项来处理实际消息:这两个选项在不同的场景中都有用。以上就是《C#学习教程:串口数据二进制通信协议解析器设计与分享》的全部内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场,如涉及侵权,请点击右边联系管理员删除。如需转载请注明出处: