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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM