| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| :imagesdir: ./img/ |
| |
| == S7 Protocol Java Implementation |
| |
| The current version of the S7 protocol java driver is based upon Netty 4 (http://netty.io/). |
| |
| As the S7 protocol running on TCP is a hierarchy of protocols, the S7 driver is implemented by providing several Protocol implementations, each encoding one layer of the protocol stack. |
| |
| The complete pipeline for the S7 protocol looks like this: |
| |
| [blockdiag,s7-netty-pipeline] |
| .... |
| { |
| Application -> S7PlcConnection; |
| Socket -> PLC; |
| |
| group { |
| label = "PLC4X S7 Driver" |
| color = "#77FF77"; |
| |
| S7PlcConnection -> Plc4XS7Protocol -> S7Protocol -> IsoTPProtocol -> IsoOnTcpProtocol -> Socket |
| |
| group { |
| Plc4XS7Protocol; S7Protocol; IsoTPProtocol; IsoOnTcpProtocol; |
| } |
| } |
| } |
| .... |
| |
| Each protocol layer implementation extends the class `MessageToMessageCodec` and is configured to consume/encode messages in the current format into messages of the next lower layer. |
| |
| The highest layer hereby consumes PLC4X messages, which are defined in the `plc4j-api` module, and the lowest layer produces a simple byte output. |
| |
| In order to implement the S7 Protocol as described in link:../../../protocols/s7/index.html[S7 Protocol] internally this is what happens inside the driver as soon as a connection is requested: |
| |
| [seqdiag,s7-netty-setup-communication] |
| .... |
| { |
| Application; S7PlcConnection; Plc4XS7Protocol; S7Protocol; IsoTPProtocol; IsoOnTcpProtocol; Socket; PLC; |
| |
| group Pipeline { |
| "Plc4XS7Protocol"; |
| "S7Protocol"; |
| "IsoTPProtocol"; |
| "IsoOnTcpProtocol"; |
| } |
| |
| Application -> S7PlcConnection [label = "Calls 'connect'"] |
| S7PlcConnection --> IsoTPProtocol [label = "Fires S7ConnectionEvent(INITIAL) event to the pipeline", note = "IsoTPProtocol listens for S7ConnectionEvent(INITIAL) events"] |
| IsoTPProtocol -> IsoOnTcpProtocol [label = "Creates new IsoOnTcp\nconnection request message"] |
| IsoOnTcpProtocol -> Socket [label = "Creates the byte array\ncontaining the TCP on ISO\nmessage"] |
| Socket -> PLC [label = "Sends the raw byte data to\nthe PLC"] |
| |
| Socket <- PLC [label = "Sends the response to the\nSocket"] |
| IsoOnTcpProtocol <- Socket [label = "Passes the binary data to\nthe pipeline"] |
| IsoTPProtocol <- IsoOnTcpProtocol [label = "Creates new IsoOnTcp message containing binary user data"] |
| |
| IsoTPProtocol --> S7Protocol [label = "Sends S7ConnectionEvent(\nISO_TP_CONNECTION_RESPONSE_\nRECEIVED) event to the\npipeline", note = "S7Protocol listens for S7ConnectionEvent(ISO_TP_CONNECTION_RESPONSE_RECEIVED) events"] |
| |
| IsoTPProtocol <- S7Protocol [label = "Creates new ISO TP Data\nmessage containing an\n S7 SetupCommunictaion\nmessage"] |
| IsoTPProtocol -> IsoOnTcpProtocol [label = "Creates new IsoOnTcp message"] |
| IsoOnTcpProtocol -> Socket [label = "Creates the byte array\ncontaining the TCP on ISO\nmessage"] |
| Socket -> PLC [label = "Sends the raw byte data to\nthe PLC"] |
| |
| Socket <-- PLC [label = "Sends the response to the\nSocket"] |
| IsoOnTcpProtocol <- Socket [label = "Passes the binary data to\nthe pipeline"] |
| IsoTPProtocol <- IsoOnTcpProtocol [label = "Creates new IsoOnTcp message containing binary user data"] |
| IsoTPProtocol -> S7Protocol |
| S7PlcConnection <- S7Protocol [label = "Sends S7ConnectionEvent(SETUP_COMPLETE) event to the\npipeline", note = "S7PlcConnection listens for S7ConnectionEvent(SETUP_COMPLETE) events"] |
| |
| } |
| .... |
| |
| Above picture is a simplification. The communication of the outside with the pipeline is not implemented in a way that one component directly addresses the other. |
| It is more that all communication is done with the `pipeline` and Netty then takes care of calling the right component at the right time. |
| |
| When writing to the pipeline from an application Netty starts at the `top` of the pipeline and asks each pipeline level if it is able and willing to handle the current message. |
| If it does, the message is passed in to the `encode` method and the next component is supplied with the output of this. |
| If the component can't process the message the next level is checked with the unmodified message. |
| |
| When reading from the Socket the Netty processes the pipeline elements the same way, but in the opposite direction and a pipeline elements `decode` method is called instead of `encode`. |
| |
| In addition to the normal flow of messages, a Netty pipeline also provides a messaging system. Every component can fire so-called `user events` and every component can react on them by implementing a `userEventTriggered` method. |
| |
| This functionality is mainly used during connection setup to synchronize the sending of messages on the different layers of the protocol stack. |