|
@@ -0,0 +1,193 @@
|
|
|
+## **PanaCIM松下扣料机服务**
|
|
|
+
|
|
|
+#### **项目介绍**
|
|
|
+生产车间拥有松下扣料机设备,需要采集读取多个送下设备生产报文,数据用于MES WMS计算生产排产和报表等功能。
|
|
|
+
|
|
|
+#### **工作内容**
|
|
|
+搭建TCP Server框架,并在现场调通设备通信。
|
|
|
+
|
|
|
+#### **技术栈**
|
|
|
+C# .NET Core TCP通信框架: DotNetty
|
|
|
+
|
|
|
+#### **成果**
|
|
|
+目前在江苏天宝汽车电子有限公司生产服务器局域网运行。
|
|
|
+
|
|
|
+#### **难点**
|
|
|
+该程序将会启用一个TCP服务器,车间内的松下设备会是客户端全部与服务器进行连接,同时不断发送XML报文。需要将XML报文拼成一个完整的报文发送到接下来的Channel进行处理。
|
|
|
+再此期间需要编写解码器应对报文的沾包,断包,多包等问题。
|
|
|
+
|
|
|
+相关:https://cybersicko.net/article/25.html
|
|
|
+
|
|
|
+#### **代码片段(解码器)**
|
|
|
+```C#
|
|
|
+using DotNetty.Buffers;
|
|
|
+using DotNetty.Codecs;
|
|
|
+using DotNetty.Transport.Channels;
|
|
|
+using Newtonsoft.Json;
|
|
|
+using Newtonsoft.Json.Linq;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+using System.Xml;
|
|
|
+
|
|
|
+namespace He3.PanaCIM.SocketServer.Handler
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// 解码
|
|
|
+ /// </summary>
|
|
|
+ public class DecoderHandler : ByteToMessageDecoder
|
|
|
+ {
|
|
|
+ private static log4net.ILog log = log4net.LogManager.GetLogger("Log.DecoderHandler");
|
|
|
+ private IByteBuffer byteBuffer = ByteBufferUtil.DefaultAllocator.Buffer();
|
|
|
+
|
|
|
+ protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
|
|
+ {
|
|
|
+ int inputLen = input.ReadableBytes;
|
|
|
+ byte[] inputBytes = new byte[inputLen];
|
|
|
+ input.GetBytes(0, inputBytes);
|
|
|
+ log.Debug(context.Channel.RemoteAddress.ToString() + " Received: " + Encoding.Default.GetString(inputBytes));
|
|
|
+
|
|
|
+ #region 处理心跳
|
|
|
+ int pingReqBegin = ByteUtil.Search(inputBytes, ByteUtil.PING_REQ);
|
|
|
+ if (pingReqBegin > -1)
|
|
|
+ {
|
|
|
+ int pingReqEnd = pingReqBegin + ByteUtil.PING_REQ_LEN;
|
|
|
+ if (pingReqEnd == ByteUtil.PING_REQ_LEN
|
|
|
+ && inputLen == ByteUtil.PING_REQ_LEN)
|
|
|
+ {
|
|
|
+ inputBytes = new byte[0];
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ inputBytes = inputBytes.Skip(0).Take(pingReqBegin).Concat(inputBytes.Skip(pingReqEnd)).ToArray();
|
|
|
+ log.Debug(context.Channel.RemoteAddress.ToString() + " Received after skip ping:" + Encoding.Default.GetString(inputBytes));
|
|
|
+ }
|
|
|
+
|
|
|
+ output.Add(createResponseMessage());
|
|
|
+ //context.WriteAndFlushAsync(ByteBufferUtil.DefaultAllocator.Buffer().WriteBytes(ByteUtil.PING_RSP));
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ while (inputBytes.Length > 0)
|
|
|
+ {
|
|
|
+ //log.Info("PipleLineId = " + "本次处理 : " + Encoding.Default.GetString(bytes));
|
|
|
+ int stxIndex = Array.IndexOf(inputBytes, ByteUtil.STX);
|
|
|
+ int etxIndex = Array.IndexOf(inputBytes, ByteUtil.ETX);
|
|
|
+
|
|
|
+ //缓存没有
|
|
|
+ if (byteBuffer.ReadableBytes == 0)
|
|
|
+ {
|
|
|
+ //有头
|
|
|
+ if (stxIndex != -1)
|
|
|
+ {
|
|
|
+ //有尾
|
|
|
+ if (etxIndex != -1)
|
|
|
+ {
|
|
|
+ int shoIndex = Array.IndexOf(inputBytes, ByteUtil.SOH);
|
|
|
+ byte[] bodyLen = inputBytes.Skip(stxIndex + 1).Take(shoIndex - stxIndex - 1).ToArray();
|
|
|
+ byte[] message = inputBytes.Skip(shoIndex + 1).Take(etxIndex - shoIndex - 1).ToArray();
|
|
|
+ output.Add(createDecodeMessage(bodyLen, message));
|
|
|
+
|
|
|
+ inputBytes = inputBytes.Skip(etxIndex + 1).ToArray();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //如果stxIndex 头部前有报文,丢弃前面的报文
|
|
|
+ byteBuffer.WriteBytes(inputBytes.Skip(stxIndex).ToArray());
|
|
|
+
|
|
|
+ int tmpLen = byteBuffer.ReadableBytes;
|
|
|
+ byte[] tmpBytes = new byte[tmpLen];
|
|
|
+ byteBuffer.GetBytes(0, tmpBytes);
|
|
|
+ //log.Info("PipleLineId = " + PipleLineId + "缓存1 : " + Encoding.Default.GetString(tmpBytes));
|
|
|
+
|
|
|
+ inputBytes = new byte[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //缓存为空,报文没头,跳过
|
|
|
+ inputBytes = new byte[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (stxIndex != -1)
|
|
|
+ {
|
|
|
+ if (stxIndex < etxIndex || etxIndex == -1)
|
|
|
+ {
|
|
|
+ int tmpLen = byteBuffer.ReadableBytes;
|
|
|
+ byte[] tmpBytes = new byte[tmpLen];
|
|
|
+ byteBuffer.GetBytes(0, tmpBytes);
|
|
|
+ //log.Info("PipleLineId = " + PipleLineId + "丢弃 : " + Encoding.Default.GetString(tmpBytes));
|
|
|
+
|
|
|
+ inputBytes = inputBytes.Skip(stxIndex).ToArray();
|
|
|
+ byteBuffer.Clear();
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (etxIndex != -1)
|
|
|
+ {
|
|
|
+ byteBuffer.WriteBytes(inputBytes.Take(etxIndex + 1).ToArray());
|
|
|
+
|
|
|
+ int tmpLen = byteBuffer.ReadableBytes;
|
|
|
+ byte[] tmpBytes = new byte[tmpLen];
|
|
|
+ byteBuffer.GetBytes(0, tmpBytes);
|
|
|
+ //log.Info("PipleLineId = " + PipleLineId + "缓存3 : " + Encoding.Default.GetString(tmpBytes));
|
|
|
+
|
|
|
+ int buffLen = byteBuffer.ReadableBytes;
|
|
|
+ byte[] currentBytes = new byte[buffLen];
|
|
|
+ byteBuffer.GetBytes(0, currentBytes);
|
|
|
+ int shoIndex = Array.IndexOf(currentBytes, ByteUtil.SOH);
|
|
|
+ byte[] bodyLen = currentBytes.Skip(1).Take(shoIndex - 1).ToArray();
|
|
|
+ //byte[] message = currentBytes.Skip(shoIndex + 1).SkipLast(1).ToArray();
|
|
|
+ byte[] message = currentBytes.Skip(shoIndex + 1).Take(currentBytes.Length - shoIndex - 2).ToArray();
|
|
|
+ output.Add(createDecodeMessage(bodyLen, message));
|
|
|
+
|
|
|
+ byteBuffer.Clear();
|
|
|
+ inputBytes = inputBytes.Skip(etxIndex + 1).ToArray();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //断包 进缓冲
|
|
|
+ byteBuffer.WriteBytes(inputBytes);
|
|
|
+ inputBytes = new byte[0];
|
|
|
+
|
|
|
+ int tmpLen = byteBuffer.ReadableBytes;
|
|
|
+ byte[] tmpBytes = new byte[tmpLen];
|
|
|
+ byteBuffer.GetBytes(0, tmpBytes);
|
|
|
+ //log.Info("PipleLineId = " + PipleLineId + "缓存2 : " + Encoding.Default.GetString(tmpBytes));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ input.SkipBytes(inputLen);
|
|
|
+ }
|
|
|
+
|
|
|
+ private DecodeMessage createDecodeMessage(byte[] length, byte[] message)
|
|
|
+ {
|
|
|
+ DecodeMessage decodeMessage = new DecodeMessage();
|
|
|
+ decodeMessage.Length = int.Parse(Encoding.Default.GetString(length));
|
|
|
+
|
|
|
+ int messageIdStartIndex = Array.IndexOf(message, ByteUtil.DQTS);
|
|
|
+ int messageIdEndIndex = Array.IndexOf(message, ByteUtil.DQTS, messageIdStartIndex + 1);
|
|
|
+
|
|
|
+ decodeMessage.MessageId = message.Skip(messageIdStartIndex + 1).Take(messageIdEndIndex - messageIdStartIndex - 1).ToArray();
|
|
|
+ decodeMessage.MessageBody = message;
|
|
|
+
|
|
|
+ return decodeMessage;
|
|
|
+ }
|
|
|
+
|
|
|
+ private DecodeMessage createResponseMessage()
|
|
|
+ {
|
|
|
+ DecodeMessage decodeMessage = new DecodeMessage();
|
|
|
+ decodeMessage.Length = 8;
|
|
|
+
|
|
|
+ decodeMessage.MessageId = ByteUtil.MessageId_RSP;
|
|
|
+ decodeMessage.MessageBody = ByteUtil.PING_RSP;
|
|
|
+
|
|
|
+ return decodeMessage;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|