本文已收录至:开源 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的更多相关文章

  1. DotNetty 实现 Modbus TCP 系列 (四) Client & Server

    本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 Client public class ModbusClient { public string Ip { get; } ...

  2. DotNetty 实现 Modbus TCP 系列 (二) ModbusFunction 类图及继承举例

    本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 ModbusFunction 类图如下: 如前文所述,所有请求/相应的 PDU 均继承自 ModbusFunction, ...

  3. DotNetty 实现 Modbus TCP 系列 (一) 报文类

    本文已收录至:开源 DotNetty 实现的 Modbus TCP/IP 协议 Modbus TCP/IP 报文 报文最大长度为 260 byte (ADU = 7 byte MBAP Header ...

  4. 开源 DotNetty 实现的 Modbus TCP/IP 协议

    本项目的目的是为了学习 DotNetty 与 Modbus 协议,参考 modjn 实现功能 0x01: Read Coils (读取线圈/离散量输出状态) 0x02: Read Discrete I ...

  5. 开放型Modbus/TCP 规范

    修订版 1.0,1999 年3 月29 日Andy SwalesSchneider 电气公司aswales@modicon.com目录目录............................... ...

  6. 基于STM32和W5500的Modbus TCP通讯

    在最近的一个项目中需要实现Modbus TCP通讯,而选用的硬件平台则是STM32F103和W5500,软件平台则选用IAR EWAR6.4来实现. 1.移植千的准备工作 为了实现Modbus TCP ...

  7. WCF编程系列(三)地址与绑定

    WCF编程系列(三)地址与绑定   地址     地址指定了接收消息的位置,WCF中地址以统一资源标识符(URI)的形式指定.URI由通讯协议和位置路径两部分组成,如示例一中的: http://loc ...

  8. mybatis入门系列三之类型转换器

    mybatis入门系列三之类型转换器 类型转换器介绍 mybatis作为一个ORM框架,要求java中的对象与数据库中的表记录应该对应 因此java类名-数据库表名,java类属性名-数据库表字段名, ...

  9. ldap配置系列三:grafana集成ldap

    ldap配置系列三:grafana集成ldap grafana的简介 grafana是一个类似kibana的东西,是对来自各种数据源的数据进行实时展示的平台,拥有这牛逼的外观.给一个官方的demo体验 ...

随机推荐

  1. 学习! ! ! Study! ! !

    我们是年轻人,钱不重要,前途才重要,干嘛着急挣钱啊.  学习!!!  study!!!

  2. OdnShop 发布 V1.0 正式版,完整可用的开源微商城系统

    OdnShop是基于ASP.NET 4.0+Mysql开发的开源微商城系统,我们的目标是构建一个核心完善而又轻量级的微商城平台. 本版本更新功能: 1,修正数据库操作的部分表名称的表前缀错误: 2,修 ...

  3. 2019 年起如何开始学习 ABP 框架系列文章-开篇有益

    2019 年起如何开始学习 ABP 框架系列文章-开篇有益 [[TOC]] 本系列文章推荐阅读地址为:52ABP 开发文档 https://www.52abp.com/Wiki/52abp/lates ...

  4. logstash grok内置规则

    logstash grok 内置正则 https://github.com/elastic/logstash/blob/v1.4.2/patterns/grok-patterns USERNAME [ ...

  5. Leetcode 101. Symmetric Tree(easy)

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  6. p9半幺群

    如何不理解划红线的地方?第二个划红线地方,请举一个例子 1.0不是幺元 2.f(1)=2, f(2)=1, f(3)=3, g(1)=2, g(2)=3, g(3)=1  fg不等于gf

  7. Java 数据库操作

    目录 Java数据库组织架构 下载驱动包 连接数据库 连接数据库的三个步骤 连接数据库的高开销 Statement接口介绍 PreparedStatement类 使用PreparedStatement ...

  8. 11 The superlative

    1 最高级用来表明三个或更多事物之间的关系.最高级是通过在形容词之前加 "the" 并在之后加 "-est",或在形容词之前加 "the most&q ...

  9. [转帖]FORFILES 的简单介绍。

    FORFILES https://blog.csdn.net/sandy9919/article/details/82932460 命令格式: forfiles.exe /p "D:\备份& ...

  10. cmake : undefined reference to dlopen, dlclose, dlsym and dlerror

    链接出了问题 添加头文件 #include <dlfcn.h> 添加库 target_link_libraries(PROJECT_NAME ${CMAKE_DL_LIBS})