DotNetty 实现 Modbus TCP 系列 (三) Codecs & Handler
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议
DotNetty 作为一个半成品,我们不需要关注细节的实现,只需要关注自己的业务即可,所以最主要的就是处理 Codecs 和 Handler。
所有的 Codecs 和 Handler 均直接或间接继承自 ChannelHandlerAdapter。为什么要分为 Codecs 和 Handler,个人理解是 Codecs 负责将消息解码为我们所需的对象或者将处理的结果编码,Handler 对解码得到的对象进行逻辑处理,达到职责分离的目的。
DotNetty 中可以注册多个 Codecs/Handler,入站消息按照注册的先后顺序执行,出站消息按照注册的先后逆序执行。
对于 Client 端:
- 入站:ModbusDecoder --> ModbusResponseHandler
- 出站:ModbusEncoder
对于 Server 端:
- 入站:ModbusDecoder --> ModbusRequestHandler
- 出站:ModbusEncoder
ModbusDecoder
public class ModbusDecoder : ByteToMessageDecoder
{
private bool isServerMode;
private readonly short maxFunctionCode = 0x80;
private readonly string typeName = "Karonda.ModbusTcp.Entity.Function.{0}.{1}{0}";
public ModbusDecoder(bool isServerMode)
{
this.isServerMode = isServerMode;
}
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
//Transaction Identifier + Protocol Identifier + Length + Unit Identifier + Function Code
if (input.Capacity < 2 + 2 + 2 + 1 + 1)
{
return;
}
ModbusHeader header = new ModbusHeader(input);
short functionCode = input.ReadByte();
ModbusFunction function = null;
if(Enum.IsDefined(typeof(ModbusCommand), functionCode))
{
var command = Enum.GetName(typeof(ModbusCommand), functionCode);
function = (ModbusFunction)Activator.CreateInstance(Type.GetType(string.Format(typeName, isServerMode ? "Request" : "Response", command)));
}
if (functionCode >= maxFunctionCode)
{
function = new ExceptionFunction(functionCode);
}
else if(function == null)
{
function = new ExceptionFunction(functionCode, 0x01);
}
function.Decode(input);
ModbusFrame frame = new ModbusFrame(header, function);
output.Add(frame);
}
}
ModbusDecoder 继承了 ByteToMessageDecoder。继承了 ByteToMessageDecoder 的类必须实现的唯一的抽象方法:Decode,该方法将 ByteBuffer 解析为 List,如果 List 不为空则会将该 List 传递给下一个 ChannelHandlerAdapter。
ModbusDecoder 同时为 Client 端和 Server 端使用,如果是 Server 端则将消息解析成请求类,反之如果是 Client 端则将消息解析成响应类。
ModbusResponseHandler
public class ModbusResponseHandler : SimpleChannelInboundHandler<ModbusFrame>
{
private Dictionary<ushort, ModbusFrame> responses = new Dictionary<ushort, ModbusFrame>();
protected override void ChannelRead0(IChannelHandlerContext ctx, ModbusFrame msg)
{
responses.Add(msg.Header.TransactionIdentifier, msg);
}
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
context.CloseAsync();
}
}
将接收到的响应信息加入 responses 供后续处理。
ModbusRequestHandler
public class ModbusRequestHandler : SimpleChannelInboundHandler<ModbusFrame>
{
private ModbusResponseService responseService;
public ModbusRequestHandler(ModbusResponseService responseService)
{
this.responseService = responseService;
}
protected override void ChannelRead0(IChannelHandlerContext ctx, ModbusFrame msg)
{
var function = msg.Function;
var response = responseService.Execute(function);
var header = msg.Header;
var frame = new ModbusFrame(header, response);
ctx.WriteAndFlushAsync(frame);
}
}
responseService 为一个抽象类,用来自定义处理接收到的请求并返回结果,需要在实现 Server 端时继承并实现。
public abstract class ModbusResponseService
{
public ModbusFunction Execute(ModbusFunction function)
{
if (function is ReadHoldingRegistersRequest)
{
var request = (ReadHoldingRegistersRequest)function;
return ReadHoldingRegisters(request);
}
throw new Exception("Function Not Support");
}
public abstract ModbusFunction ReadHoldingRegisters(ReadHoldingRegistersRequest request);
}
(文中代码仅添加了 0x03 的方法)
ModbusEncoder
public class ModbusEncoder : ChannelHandlerAdapter
{
public override Task WriteAsync(IChannelHandlerContext context, object message)
{
if (message is ModbusFrame)
{
var frame = (ModbusFrame)message;
return context.WriteAndFlushAsync(frame.Encode());
}
return context.WriteAsync(message);
}
}
如果是 ModbusFrame 消息则 Flush,否则传递到下一个 ChannelHandlerAdapter。
开源地址:modbus-tcp
DotNetty 实现 Modbus TCP 系列 (三) Codecs & Handler的更多相关文章
- DotNetty 实现 Modbus TCP 系列 (四) Client & Server
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 Client public class ModbusClient { public string Ip { get; } ...
- DotNetty 实现 Modbus TCP 系列 (二) ModbusFunction 类图及继承举例
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 ModbusFunction 类图如下: 如前文所述,所有请求/相应的 PDU 均继承自 ModbusFunction, ...
- DotNetty 实现 Modbus TCP 系列 (一) 报文类
本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 Modbus TCP/IP 报文 报文最大长度为 260 byte (ADU = 7 byte MBAP Header ...
- 开源 DotNetty 实现的 Modbus TCP/IP 协议
本项目的目的是为了学习 DotNetty 与 Modbus 协议,参考 modjn 实现功能 0x01: Read Coils (读取线圈/离散量输出状态) 0x02: Read Discrete I ...
- 开放型Modbus/TCP 规范
修订版 1.0,1999 年3 月29 日Andy SwalesSchneider 电气公司aswales@modicon.com目录目录............................... ...
- 基于STM32和W5500的Modbus TCP通讯
在最近的一个项目中需要实现Modbus TCP通讯,而选用的硬件平台则是STM32F103和W5500,软件平台则选用IAR EWAR6.4来实现. 1.移植千的准备工作 为了实现Modbus TCP ...
- WCF编程系列(三)地址与绑定
WCF编程系列(三)地址与绑定 地址 地址指定了接收消息的位置,WCF中地址以统一资源标识符(URI)的形式指定.URI由通讯协议和位置路径两部分组成,如示例一中的: http://loc ...
- mybatis入门系列三之类型转换器
mybatis入门系列三之类型转换器 类型转换器介绍 mybatis作为一个ORM框架,要求java中的对象与数据库中的表记录应该对应 因此java类名-数据库表名,java类属性名-数据库表字段名, ...
- ldap配置系列三:grafana集成ldap
ldap配置系列三:grafana集成ldap grafana的简介 grafana是一个类似kibana的东西,是对来自各种数据源的数据进行实时展示的平台,拥有这牛逼的外观.给一个官方的demo体验 ...
随机推荐
- hdu2121 Ice_cream's world II
hdu2121 Ice_cream's world II 给一个有向图,求最小树形图,并输出根节点 \(n\leq10^3,\ m\leq10^4\) 最小树形图 对于求无根最小树形图,可以建一个虚拟 ...
- P4550 收集邮票-洛谷luogu
传送门 题目描述 有n种不同的邮票,皮皮想收集所有种类的邮票.唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n.但是由于凡凡也很喜欢 ...
- java 变量 final 小结
通过查看hashCode发现,变量声明final后,不能修改,上级修改时候,重新获得对象hashCode变化 public static void main(String[] args) { // T ...
- ML.NET 示例:聚类之客户细分
写在前面 准备近期将微软的machinelearning-samples翻译成中文,水平有限,如有错漏,请大家多多指正. 如果有朋友对此感兴趣,可以加入我:https://github.com/fei ...
- zookeeper-监控与优化-《每日五分钟搞定大数据》
本文的命令和配置都是基于zookeeper-3.4.6版本.优化很多时候都是基于监控的,所以把这两个内容写在了一起,慢慢消化. 监控 简单地说,监控无非就是获取服务的一些指标,再根据实际业务情况给这些 ...
- SkyWalking Liunx 环境搭建&NetCore接入
背景 前两天看见有小哥介绍windows下安装skywalking的介绍地址在这. 正好最近也在搭建linux环境的SkyWalking,顺便把linux环境搭建的经验分享下,帮助下使用linux部署 ...
- 朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上)
朱晔的互联网架构实践心得S1E7:三十种架构设计模式(上) [下载本文PDF进行阅读] 设计模式是前人通过大量的实践总结出来的一些经验总结和最佳实践.在经过多年的软件开发实践之后,回过头来去看23种设 ...
- sql面试学到新内容
1.事物的保存点 MYSQL可以让我们对事务进行部分回滚,就是在事务里调用SAVEPOINT语句来设置一些命名标记.如果想要回滚到那个标记点位置,需要使用ROLLBACK语句来指定哪个保存点. mys ...
- Python-Requests库详解
查看一下是否安装requests库 什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关 ...
- Prometheus & SoundCloud
Prometheus 系统监控方案 一 - Vovolie - 博客园https://www.cnblogs.com/vovlie/p/Prometheus_CONCEPTS.html Prometh ...