0


工业4.0通信协议学习之Hermes

工业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.上下游状态机主要代码示例

usingPrism.Mvvm;namespaceHermesLibrary.Models{publicclassHermesStatus:BindableBase{publicHermesStatus(){}publicbool bFromUp {get;set;}publicbool bFrontLane {get;set;}privateEHermesState _State;publicEHermesState HermesState
        {get{return _State;}set{
                _State =value;RaisePropertyChanged();}}privatebool _bSocketConnected;publicbool 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);}elseCallStatusChangedToDown(HermesState, bFrontLane);}}else{
                    HermesState = EHermesState.eHERMES_STATE_NOT_CONNECTED;if(bFromUp)CallStatusChangedToUp(HermesState, bFrontLane);elseCallStatusChangedToDown(HermesState, bFrontLane);}}}privatebool _bServiceDescriptionDownstream =false;publicbool 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);}}}privatebool _bServiceDescriptionUpstream =false;publicbool 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);}}}privatebool _bBoardForecast =false;publicbool 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);}elseif(HermesState == EHermesState.eHERMES_STATE_MACHINE_READY){
                        HermesState = EHermesState.eHERMES_STATE_MACHINE_READY;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}}}}privatebool _bMachineReady =false;publicbool 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);}elseif(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);}elseif(HermesState == EHermesState.eHERMES_STATE_MACHINE_READY)// RevokeMachineReady{
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;if(bFromUp)CallStatusChangedToUp(HermesState, bFrontLane);}}}}privatebool _bBoardAvailable =false;publicbool 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);}elseif(HermesState == EHermesState.eHERMES_STATE_MACHINE_READY){
                        HermesState = EHermesState.eHERMES_STATE_AVAILABLE_AND_READY;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED){
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_STOPPED;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORTING){
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORTING;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}}else{// RevokeBoardAvailableif(HermesState == EHermesState.eHERMES_STATE_BOARD_AVAILABLE){
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}elseif(HermesState == EHermesState.eHERMES_STATE_AVAILABLE_AND_READY){
                        HermesState = EHermesState.eHERMES_STATE_MACHINE_READY;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED){
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORT_STOPPED;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORTING){
                        HermesState = EHermesState.eHERMES_STATE_TRANSPORTING;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}}}}privatebool _bStartTransport =false;publicbool 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);}}}}privatebool _bStopTransport =false;publicbool 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);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORT_FINISHED){
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;if(bFromUp)CallStatusChangedToUp(HermesState, bFrontLane);}}}}privatebool _bTransportFinished =false;publicbool 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);}elseif(HermesState == EHermesState.eHERMES_STATE_TRANSPORT_STOPPED){
                        HermesState = EHermesState.eHERMES_STATE_NOT_AVAILABLE_NOT_READY;if(!bFromUp)CallStatusChangedToDown(HermesState, bFrontLane);}}}}#region EventspublicdelegatevoidStatusChangedEventHandler(object sender,EHermesState e,bool bfrontLane);publiceventStatusChangedEventHandler OnHermesStatusChangedToUp;protectedvirtualvoidCallStatusChangedToUp(EHermesState e,bool bfrontLane){
            OnHermesStatusChangedToUp?.Invoke(this, e,  bfrontLane);}publiceventStatusChangedEventHandler OnHermesStatusChangedToDown;protectedvirtualvoidCallStatusChangedToDown(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 voidHermesStatusToUpR_OnHermesStatusChangedToUp(object sender, EHermesState e, bool bfrontLane){if(bfrontLane){#regionUpStream FrontLaneif(HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_NOT_CONNECTED)){#regionDisconnect// Stop CheckAlive Timer// TODO...#endregion}elseif(HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_SOCKET_CONNECTED)){#regionConnect// Start CheckAlive Timer// TODO...#endregion}elseif(HermesStatusToUpFront.bServiceDescriptionDownstream && HermesStatusToUpFront.bSocketConnected){#regionServiceDescriptionDownstream// TODO...// SendToUpCmd(ServiceDescriptionDownstream);#endregion}elseif(HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_MACHINE_READY)){#regionMachineReady// TODO...// SendToUpCmd(MachineReady);#endregion}elseif(HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_TRANSPORTING)){#regionStartTransport// TODO...#endregion}elseif(HermesStatusToUpFront.HermesState.Equals(EHermesState.eHERMES_STATE_TRANSPORT_STOPPED)){#regionStopTransport// TODO...#endregion}#endregion}else{#regionUpStream RearLane#endregion}}
        private voidHandleCmdFromUp(object sender, TcpDatagramReceivedEventArgs<byte[]> e){
            string xml = Encoding.UTF8.GetString(e.Datagram);
            Enum_CMD CmdFromUp = new Enum_CMD();// This value is from xmlswitch(CmdFromUp){case Enum_CMD.CheckAlive:#regionCheckAlive// SendPong#endregionbreak;case Enum_CMD.ServiceDescription:#regionServiceDescription
                    HermesStatusToUpFront.bServiceDescriptionDownstream = true;#endregionbreak;case Enum_CMD.Notification:#regionNotification// Disconnect with UpStream// Stop CheckAlive Timer#endregionbreak;case Enum_CMD.BoardAvailable:#regionBoardAvailable
                    HermesStatusToUpFront.bBoardAvailable = true;#endregionbreak;case Enum_CMD.RevokeBoardAvailable:#regionRevokeBoardAvailable
                    HermesStatusToUpFront.bBoardAvailable = false;#endregionbreak;case Enum_CMD.TransportFinished:#regionTransportFinished
                    HermesStatusToUpFront.bTransportFinished = true;#endregionbreak;case Enum_CMD.SendBoardInfo:#regionSendBoardInfo// UpStream send SendBoardInfo after receiving QueryBoardInfo#endregionbreak;case Enum_CMD.BoardForecast:#regionBoardForecast
                    HermesStatusToUpFront.bBoardForecast = true;#endregionbreak;case Enum_CMD.Command:#regionCommand#endregionbreak;default:break;}}

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

publicHermesConnectionDownStream(IPEndPoint localEP){
            iPEndPoint = localEP;
            FrontLaneServer =newAsyncTcpServerHermes(localEP);
            FrontLaneServer.DataReceived += HandleCmdFromDown;
            HermesStatusToDownFront =newHermesStatus{
                bFromUp =false,
                bFrontLane =true};
            HermesStatusToDownFront.OnHermesStatusChangedToDown += HermesStatusToDownFront_OnHermesStatusChangedToDown;}privatevoidHermesStatusToDownFront_OnHermesStatusChangedToDown(object sender,EHermesState e,bool bfrontLane){if(bfrontLane){#region DownStream FrontLaneif(HermesStatusToDownFront.HermesState.Equals(EHermesState.eHERMES_STATE_NOT_CONNECTED)){#region Disconnect// TODO...#endregion}elseif(HermesStatusToDownFront.HermesState.Equals(EHermesState.eHERMES_STATE_SOCKET_CONNECTED)){#region Connect// TODO...#endregion}elseif(HermesStatusToDownFront.bServiceDescriptionUpstream && HermesStatusToDownFront.bSocketConnected){#region ServiceDescriptionUpstream// TODO...,send to downstream#endregion}elseif(HermesStatusToDownFront.bBoardForecast){#region BoardForecastBoardForecast boardForecast =newBoardForecast();string BoardForecastxml =string.Empty;// convert BoardForecast to xml stringSendToDownCmd(BoardForecastxml);// TODO...#endregion}elseif(HermesStatusToDownFront.bBoardAvailable){#region BoardAvailable// TODO...#endregion}elseif(HermesStatusToDownFront.bTransportFinished){#region TransportFinished// TODO...#endregion}#endregion}else{#region DownStream RearLane#endregion}}privatevoidHandleCmdFromDown(object sender,AsyncEventArgs e){string xml = Encoding.UTF8.GetString(e._state.Buffer);Enum_CMD CmdFromDown =newEnum_CMD();// This value is from xmlswitch(CmdFromDown){case Enum_CMD.CheckAlive:#region CheckAlive// Send Pong#endregionbreak;case Enum_CMD.QueryBoardInfo:#region QueryBoardInfoSendBoardInfo info =newSendBoardInfo();///#endregionbreak;case Enum_CMD.Notification:#region Notification// stop the client...#endregionbreak;case Enum_CMD.ServiceDescription:#region ServiceDescription
                    HermesStatusToDownFront.bServiceDescriptionDownstream =true;#endregionbreak;case Enum_CMD.MachineReady:#region MachineReady
                    HermesStatusToDownFront.bMachineReady =true;#endregionbreak;case Enum_CMD.RevokeMachineReady:#region MachineReady
                    HermesStatusToDownFront.bMachineReady =false;#endregionbreak;case Enum_CMD.StartTransport:#region StartTransport
                    HermesStatusToDownFront.bStartTransport =true;#endregionbreak;case Enum_CMD.StopTransport:#region StopTransport
                    HermesStatusToDownFront.bStopTransport =true;#endregionbreak;case Enum_CMD.Command:#region Command#endregionbreak;default:break;}}

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

publicHermesConnectionVertical(IPEndPoint IPEndPoint){
            VerticalClient =newAsyncTcpClientHermes(IPEndPoint);
            VerticalClient.DatagramReceived += HandleCmdFromVertical;}privatevoidHandleCmdFromVertical(object sender,TcpClientWrapper.TcpDatagramReceivedEventArgs<byte[]> e){string xml = Encoding.UTF8.GetString(e.Datagram);Enum_CMD CmdFromUp =newEnum_CMD();// This value is from xmlswitch(CmdFromUp){case Enum_CMD.SendWorkOrderInfo:#region SendWorkOrderInfo#endregionbreak;case Enum_CMD.ReplyWorkOrderInfo:#region ReplyWorkOrderInfo#endregionbreak;case Enum_CMD.SupervisoryServiceDescription:#region SupervisoryServiceDescription// reply#endregionbreak;default:break;}}

总结

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

标签: 学习 c# 大数据

本文转载自: https://blog.csdn.net/Flora1992/article/details/127003853
版权归原作者 Flora*.* 所有, 如有侵权,请联系我们删除。

“工业4.0通信协议学习之Hermes”的评论:

还没有评论