PanaCIM松下扣料机服务.md 8.1 KB

PanaCIM松下扣料机服务

项目介绍

生产车间拥有松下扣料机设备,需要采集读取多个送下设备生产报文,数据用于MES WMS计算生产排产和报表等功能。

工作内容

搭建TCP Server框架,并在现场调通设备通信。

技术栈

C# .NET Core TCP通信框架: DotNetty

成果

目前在江苏天宝汽车电子有限公司生产服务器局域网运行。

难点

该程序将会启用一个TCP服务器,车间内的松下设备会是客户端全部与服务器进行连接,同时不断发送XML报文。需要将XML报文拼成一个完整的报文发送到接下来的Channel进行处理。 再此期间需要编写解码器应对报文的沾包,断包,多包等问题。

相关:https://cybersicko.net/article/25.html

代码片段(解码器)

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;
        }
    }
}