工业4.0通信协议学习之Hermes

前言

随着大数据和工业4.0的发展,对于设备生产商而言,所生产的设备需要支持各种通信协议,因此,设备开发的软件工程师也需要学习各种通信协议。本文用于记录Hermes协议的学习。

一、Hermes协议适用范围

Hermes通信协议用于在表面贴装技术 (SMT) 生产线上处理电路板传输和相关数据。该协议是SMEMA协议的升级版和替代版,因此主要用于相邻机器(M2M)之间的信息传输,使用TCP协议进行连接。

二、如何通信

对于设备软件开发而言,主要关注的点是与谁通信,如何通信,交互什么数据,什么时候交互?

1. 与谁通信

前面介绍了Hermes协议用于相邻机器之间的信息交互,因此对于一台设备而言,主要是与上下游机进行通信,在Hermes协议中把这定义为Horizontal Channel;另一个是与Supervisory System通信,在Hermes协议中叫做Vertical Channel。Hermes通信

2. 如何通信

1) 与上游机通信

与上游机通信时,上游机的每个轨道是一个TCP Server,它会提供IP和端口号,我们设备只需要监听该端口号就可以实现通信。

2) 与下游机通信

与下游机通信时,我们的设备的每个轨道作为一个TCP Server,提供IP和端口号,供下游机连接。

3) 与Supervisory System通信

与Supervisory System通信时,我们设备作为TCP Client,去监听Supervisory System提供的Server。

3. 交互什么数据

在Hermes协议中定义了很多消息(Message),如CheckAlive、ServiceDescription、Notification等等。这些消息包含了设备信息、生产信息等等,通过Hermes协议进行传输,并控制生产,哪些消息是传到上游的,哪些是传到下游的,协议中做了详细规定。
注意,协议已经规定了消息传输的格式为XML格式,因此所有数据必须生成XML格式之后才能进行传输,如下图为CheckAlive消息的格式。
在这里插入图片描述

4. 什么时候交互

这里要Hermes协议中非常重要的一个部分,Hermes状态机,如下图,所有的消息都是在状态切换时才会发送。Hermes状态机
每个TCP连接都会有一个状态机,如图中所示,Hermes状态分为9种:NotConnected、ServiceDescription、NotAvaliableNotReady、BoardAvaliable、AvaliableAndReady、MachineReady、TransportStopped、Transporting、TransportFinished。每个状态切换时都有先提条件,例如从NotConnected–>ServiceDescriptionDownStream,需要当前Hermes状态为NotConnected,且当前正好本机与上游机Lane 1的Socket连接通讯成功,那么此时该连接的Hermes状态变成ServiceDescriptionDownStream,此时根据上图状态机,本机需要发送ServiceDescription消息给上游机Lane 1。
除了与上下游机的Hermes状态机外,Hermes协议还规定了本机与SuperVisory System连接的状态机,如下图所示:
在这里插入图片描述

三、代码示例

代码只是简单列了一下,收到消息该做哪些动作,并没有写具体操作,真正用在项目中还需要根据协议进行补充和修改。

1.上下游状态机主要代码示例

using Prism.Mvvm;

namespace HermesLibrary.Models
{
    public class HermesStatus : BindableBase
    {
        public HermesStatus()
        {

        }
        public bool bFromUp { get; set; }
        public bool bFrontLane { get; set; }
        private EHermesState _State;
        public EHermesState HermesState
        {
            get { return _State; }
            set
            {
                _State = value;
                RaisePropertyChanged();
            }
        }
        private bool _bSocketConnected;
        public bool bSocketConnected
        {
            get
            {
                return _bSocketConnected;
            }
            set
            {
                _bSocketConnected = value;
                RaisePropertyChanged();
                if (_bSocketConnected)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_NOT_CONNECTED)
                    {
                        HermesState = EHermesState.eHERMES_STATE_SOCKET_CONNECTED;
                        if (bFromUp)
                        {
                            bServiceDescriptionDownstream = true;
                            CallStatusChangedToUp(HermesState, bFrontLane);
                        }
                        else
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                }
                else
                {
                    HermesState = EHermesState.eHERMES_STATE_NOT_CONNECTED;
                    if(bFromUp)
                        CallStatusChangedToUp(HermesState, bFrontLane);
                    else
                        CallStatusChangedToDown(HermesState, bFrontLane);
                }
            }
        }

        private bool _bServiceDescriptionDownstream = false;
        public bool bServiceDescriptionDownstream
        {
            get { return _bServiceDescriptionDownstream; }
            set
            {
                _bServiceDescriptionDownstream = value;
                RaisePropertyChanged();
                if (HermesState == EHermesState.eHERMES_STATE_NOT_CONNECTED)
                {
                    HermesState = EHermesState.eHERMES_STATE_SERVICE_DESCRIPTION_DOWNSTREAM;
                    if (bFromUp)
                        CallStatusChangedToUp(HermesState, bFrontLane);
                }
            }
        }

        private bool _bServiceDescriptionUpstream = false;
        public bool bServiceDescriptionUpstream
        {
            get { return _bServiceDescriptionUpstream; }
            set
            {
                _bServiceDescriptionUpstream = value;
                RaisePropertyChanged();
                if (_bServiceDescriptionUpstream && HermesState == EHermesState.eHERMES_STATE_SERVICE_DESCRIPTION_DOWNSTREAM)
                {
                    HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                    if (!bFromUp)
                        CallStatusChangedToDown(HermesState, bFrontLane);
                }
            }
        }

        private bool _bBoardForecast = false;
        public bool bBoardForecast
        {
            get { return _bBoardForecast; }
            set
            {
                _bBoardForecast = value;
                RaisePropertyChanged();
                if (_bBoardForecast)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_MACHINE_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_MACHINE_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                }
      
            }
        }

        private bool _bMachineReady = false;
        public bool bMachineReady
        {
            get { return _bMachineReady; }
            set
            {
                _bMachineReady = value;
                RaisePropertyChanged();
                if (_bMachineReady)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_MACHINE_READY;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_AVAILABLE_AND_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_BOARD_AVAILABLE;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                }
                else
                {
                    if (HermesState == EHermesState.eHERMES_STATE_AVAILABLE_AND_READY)// RevokeMachineReady
                    {
                        HermesState = EHermesState.eHERMES_STATE_BOARD_AVAILABLE;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_MACHINE_READY)// RevokeMachineReady
                    {
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                }
            }
        }

        private bool _bBoardAvailable = false;
        public bool bBoardAvailable
        {
            get { return _bBoardAvailable; }
            set
            {
                _bBoardAvailable = value;
                RaisePropertyChanged();
                if (_bBoardAvailable)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_BOARD_AVAILABLE;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_MACHINE_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_AVAILABLE_AND_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_STOPPED;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORTING)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORTING;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                }
                else
                {// RevokeBoardAvailable
                    if (HermesState == EHermesState.eHERMES_STATE_BOARD_AVAILABLE)
                    {
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_AVAILABLE_AND_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_MACHINE_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_STOPPED;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORTING)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORTING;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                }
               
            }
        }

        private bool _bStartTransport = false;
        public bool bStartTransport
        {
            get { return _bStartTransport; }
            set
            {
                _bStartTransport = value;
                RaisePropertyChanged();
                if (_bStartTransport)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_AVAILABLE_AND_READY
                        || HermesState == EHermesState.eHERMES_STATE_MACHINE_READY)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORTING;
                        if(bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    } 
                }
            }
        }

        private bool _bStopTransport = false;
        public bool bStopTransport
        {
            get { return _bStopTransport; }
            set
            {
                _bStopTransport = value;
                RaisePropertyChanged();
                if (_bStopTransport)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_TRANSPORTING)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_STOPPED;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORT_FINISHED)
                    {
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                        if (bFromUp)
                            CallStatusChangedToUp(HermesState, bFrontLane);
                    }
                }
            }
        }

        private bool _bTransportFinished = false;
        public bool bTransportFinished
        {
            get { return _bTransportFinished; }
            set
            {
                _bTransportFinished = value;
                RaisePropertyChanged();
                if (_bTransportFinished)
                {
                    if (HermesState == EHermesState.eHERMES_STATE_TRANSPORTING)
                    {
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_FINISHED;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                    else if (HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED)
                    {
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;
                        if (!bFromUp)
                            CallStatusChangedToDown(HermesState, bFrontLane);
                    }
                }
            }
        }

        #region Events
        public delegate void StatusChangedEventHandler(object sender, EHermesState e,bool bfrontLane);

        public event StatusChangedEventHandler OnHermesStatusChangedToUp;
        protected virtual void CallStatusChangedToUp(EHermesState e, bool bfrontLane)
        {
            OnHermesStatusChangedToUp?.Invoke(this, e,  bfrontLane);
        }
        public event StatusChangedEventHandler OnHermesStatusChangedToDown;
        protected virtual void CallStatusChangedToDown(EHermesState e, bool bfrontLane)
        {
            OnHermesStatusChangedToDown?.Invoke(this, e, bfrontLane);
        }
        #endregion
    }
}

2.与上游通信代码示例

    public HermesConnectionUpStream(bool bDualLane, IPEndPoint frontLaneIPEndPoint, IPEndPoint rearLaneIPEndPoint)
        {
            FrontLaneClient = new AsyncTcpClientHermes(frontLaneIPEndPoint);
            FrontLaneClient.DatagramReceived += HandleCmdFromUp;
            HermesStatusToUpFront = new HermesStatus
            {
                bFromUp = true,
                bFrontLane = true
            };// Upstream Lane 1
            HermesStatusToUpFront.OnHermesStatusChangedToUp += HermesStatusToUpR_OnHermesStatusChangedToUp;
            if (bDualLane)
                RearLaneClient = new AsyncTcpClientHermes(rearLaneIPEndPoint);
        }

        private void HermesStatusToUpR_OnHermesStatusChangedToUp(object sender, EHermesState e, bool bfrontLane)
        {
            if (bfrontLane)
            {
                #region UpStream FrontLane
                if (HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_NOT_CONNECTED))
                {
                    #region Disconnect
                    // Stop CheckAlive Timer
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_SOCKET_CONNECTED))
                {
                    #region Connect
                    // Start CheckAlive Timer
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToUpFront.bServiceDescriptionDownstream && HermesStatusToUpFront.bSocketConnected)
                {
                    #region ServiceDescriptionDownstream
                    // TODO...
                    // SendToUpCmd(ServiceDescriptionDownstream);
                    #endregion
                }
                else if (HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_MACHINE_READY))
                {
                    #region MachineReady
                    // TODO...
                    // SendToUpCmd(MachineReady);
                    #endregion
                }
                else if (HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_TRANSPORTING))
                {
                    #region StartTransport
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_TRANSPORT_STOPPED))
                {
                    #region StopTransport
                    // TODO...
                    #endregion
                }
                #endregion
            }
            else
            {
                #region UpStream RearLane

                #endregion
            }
        }
        private void HandleCmdFromUp(object sender, TcpDatagramReceivedEventArgs<byte[]> e)
        {
            string xml = Encoding.UTF8.GetString(e.Datagram);
            Enum_CMD CmdFromUp = new Enum_CMD();// This value is from xml
            switch (CmdFromUp)
            {
                case Enum_CMD.CheckAlive:
                    #region CheckAlive
                    // SendPong
                    #endregion
                    break;

                case Enum_CMD.ServiceDescription:
                    #region ServiceDescription
                    HermesStatusToUpFront.bServiceDescriptionDownstream = true;
                    #endregion
                    break;

                case Enum_CMD.Notification:
                    #region Notification
                    // Disconnect with UpStream
                    // Stop CheckAlive Timer
                    #endregion
                    break;

                case Enum_CMD.BoardAvailable:
                    #region BoardAvailable
                    HermesStatusToUpFront.bBoardAvailable = true;
                    #endregion
                    break;

                case Enum_CMD.RevokeBoardAvailable:
                    #region RevokeBoardAvailable
                    HermesStatusToUpFront.bBoardAvailable = false;
                    #endregion
                    break;

                case Enum_CMD.TransportFinished:
                    #region TransportFinished
                    HermesStatusToUpFront.bTransportFinished = true;
                    #endregion
                    break;

                case Enum_CMD.SendBoardInfo:
                    #region SendBoardInfo
                    // UpStream send SendBoardInfo after receiving QueryBoardInfo
                    #endregion
                    break;

                case Enum_CMD.BoardForecast:
                    #region BoardForecast
                    HermesStatusToUpFront.bBoardForecast = true;
                    #endregion
                    break;

                case Enum_CMD.Command:
                    #region Command

                    #endregion
                    break;


                default:
                    break;
            }
        }

3. 与下游机通信的代码示例

        public HermesConnectionDownStream(IPEndPoint localEP)
        {
            iPEndPoint = localEP;
            FrontLaneServer = new AsyncTcpServerHermes(localEP);
            FrontLaneServer.DataReceived += HandleCmdFromDown;
            HermesStatusToDownFront = new HermesStatus
            {
                bFromUp = false,
                bFrontLane = true
            };
            HermesStatusToDownFront.OnHermesStatusChangedToDown += HermesStatusToDownFront_OnHermesStatusChangedToDown;
        }

        private void HermesStatusToDownFront_OnHermesStatusChangedToDown(object sender, EHermesState e, bool bfrontLane)
        {
            if (bfrontLane)
            {
                #region DownStream FrontLane
                if (HermesStatusToDownFront.HermesState.Equals(EHermesState.eHERMES_STATE_NOT_CONNECTED))
                {
                    #region Disconnect
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToDownFront.HermesState.Equals(EHermesState.eHERMES_STATE_SOCKET_CONNECTED))
                {
                    #region Connect
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToDownFront.bServiceDescriptionUpstream && HermesStatusToDownFront.bSocketConnected)
                {
                    #region ServiceDescriptionUpstream
                    // TODO...,send to downstream
                    #endregion
                }
                else if (HermesStatusToDownFront.bBoardForecast)
                {
                    #region BoardForecast
                    BoardForecast boardForecast = new BoardForecast();
                    string BoardForecastxml = string.Empty;// convert BoardForecast to xml string
                    SendToDownCmd(BoardForecastxml);
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToDownFront.bBoardAvailable)
                {
                    #region BoardAvailable
                    // TODO...
                    #endregion
                }
                else if (HermesStatusToDownFront.bTransportFinished)
                {
                    #region TransportFinished
                    // TODO...
                    #endregion
                }
                #endregion
            }
            else
            {
                #region DownStream RearLane

                #endregion
            }
        }

        private void HandleCmdFromDown(object sender, AsyncEventArgs e)
        {
            string xml = Encoding.UTF8.GetString(e._state.Buffer);
            Enum_CMD CmdFromDown = new Enum_CMD();// This value is from xml
            switch (CmdFromDown)
            {
                case Enum_CMD.CheckAlive:
                    #region CheckAlive
                    // Send Pong
                    #endregion
                    break;

                case Enum_CMD.QueryBoardInfo:
                    #region QueryBoardInfo
                    SendBoardInfo info = new SendBoardInfo();
                    ///
                    #endregion
                    break;

                case Enum_CMD.Notification:
                    #region Notification
                    // stop the client...
                    #endregion
                    break;

                case Enum_CMD.ServiceDescription:
                    #region ServiceDescription
                    HermesStatusToDownFront.bServiceDescriptionDownstream = true;
                    #endregion
                    break;

                case Enum_CMD.MachineReady:
                    #region MachineReady
                    HermesStatusToDownFront.bMachineReady = true;
                    #endregion
                    break;

                case Enum_CMD.RevokeMachineReady:
                    #region MachineReady
                    HermesStatusToDownFront.bMachineReady = false;
                    #endregion
                    break;

                case Enum_CMD.StartTransport:
                    #region StartTransport
                    HermesStatusToDownFront.bStartTransport = true;
                    #endregion
                    break;

                case Enum_CMD.StopTransport:
                    #region StopTransport
                    HermesStatusToDownFront.bStopTransport = true;
                    #endregion
                    break;

                case Enum_CMD.Command:
                    #region Command
                   
                    #endregion
                    break;
                default:
                    break;
            }
        }

4. 与SuperVisory System通信的代码示例

  public HermesConnectionVertical(IPEndPoint IPEndPoint)
        {
            VerticalClient = new AsyncTcpClientHermes(IPEndPoint);
            VerticalClient.DatagramReceived += HandleCmdFromVertical;
        }
         private void HandleCmdFromVertical(object sender, TcpClientWrapper.TcpDatagramReceivedEventArgs<byte[]> e)
        {
            string xml = Encoding.UTF8.GetString(e.Datagram);
            Enum_CMD CmdFromUp = new Enum_CMD();// This value is from xml
            switch (CmdFromUp)
            {
                case Enum_CMD.SendWorkOrderInfo:
                    #region SendWorkOrderInfo
                 
                    #endregion
                    break;
                case Enum_CMD.ReplyWorkOrderInfo:
                    #region ReplyWorkOrderInfo

                    #endregion
                    break;
                case Enum_CMD.SupervisoryServiceDescription:
                    #region SupervisoryServiceDescription
                    // reply
                    #endregion
                    break;
                default:
                    break;
            }
        }

总结

以上就是对Hersmes协议的简单介绍,不过Hermes协议还有很多本文未体现的内容,如具体的消息,消息的参数,范围,不同版本Hermes协议中消息内容也有所不同,异常处理,这里并没有一一列出。因此,在实际项目中,还需要有配置的地方,有些消息的参数是可选的,有效需要执行的操作也是可配置的,这些都要仔细研究协议,将代码写的更加详细。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值