blob: 671f048a6253649ed43ad97408d51375ed138fec [file] [log] [blame]
//
// 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.