Snap7 Server

 

Introduction

In spite of the fact that the Snap7Server is the easier object to use, initially it is probably the most complicated to digest.

Let's start to saying that Snap7Server neither is a kind of OPC Server nor is a program that gathers data from PLC and presents the results.

Snap7Server, just like a communication processor (CP), accepts S7 connections by external clients, and replies to their requests.

 

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image006.png

 

Just like the CPU that shares its resources with its CP, Your application must should share its resources (memory blocks) with the server.

Starting from 1.4.0 Snap7Server can be work in resourceless mode (see).

The mechanism is very simple:

·         Your program allocates a memory block and says to the server “this is your DB35”. Every time a client requests to read/write some byte from/to DB35, the server uses that block.

·         If a client requests the access to an inexistent block (i.e. a block that you didn’t shared) the server replies with an error of resource not found, just like a real PLC would do.

The client does not see any difference from a real PLC.

The simulation level is quite depth: S7 Manager (or TIA Portal) itself, sees your application as a CPU 315-2PN/DP.

Online Project

 

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image007.jpg

 

Module information

 

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image008.png

 

 

Communication Info

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image009.png

 

Not being able to test Snap7Server with hundreds systems on the market, the key concept is:

Ø  If Siemens Simatic manager sees the server as a real PLC, more so every client (Scada, hmi panel, PLC driver) will see the server as a real PLC.

 

You can obtain the preceding result with the simple following program :

#include <stdio.h>

#include <stdlib.h>

#include <cstring>

#include "snap7.h"

 

     TS7Server *Server;

     unsigned char DB1[512];  // Our DB1

     unsigned char DB2[128];  // Our DB2

     unsigned char DB3[1024]; // Our DB3

     unsigned char MB[2048];  // 2048 = CPU 315 Merkers amount

 

int main(int argc, char* argv[])

{

    int Error;

    Server = new TS7Server;

    Server->RegisterArea(srvAreaDB,     // We are registering a DB

                         1,             // Its number is 1 (DB1)

                         &DB1,          // Its address

                         sizeof(DB1));  // Its size

    // Do the same for DB2 and DB3

    Server->RegisterArea(srvAreaDB, 2, &DB2, sizeof(DB2));

    Server->RegisterArea(srvAreaDB, 3, &DB3, sizeof(DB3));

    // Let’s share all Merkers from M0.0 to M2047.7

    Server->RegisterArea(srvAreaMK, 0, &MB, sizeof(MB));

 

    // Start the server onto the default adapter “0.0.0.0”

    Error=Server->Start();

    if (Error==0)

    {

       // Now the server is running ... wait a key to terminate

        getchar();

    }

    else

        printf("%s\n",SrvErrorText(Error).c_str());

 

    Server->Stop(); // not strictly needed

    delete Server;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Please refer to API reference for functions syntax.

You can find the C# and Pascal version into examples folders.

Notice that we shared also the merkers area.


 

Specifications

The Snap7Server is a multi-client multi-threaded server.

Once a connection is accepted, a new S7 worker thread is created which, from this moment will serve that client.
When the clients disconnects, the S7 worker is destroyed.

Up to 1024 (*) connection can be accepted, this value however can be changed via Srv_SetParam().

At the moment there is no Blacklist/Whitelist mechanism for filtering the connections, in a future release however it could be implemented (depending on the project audience).

The compatibility with Simatic Manager, of course, is not complete.

The server, as said, is a CP simulator not a SoftPLC, i.e. there isn’t a MC7 program, compatible with Simatic Manager, to be edited, uploaded or downloaded: the business logic, if any, is your application program.

S7 functions implemented (in the current release)

 

·         Data I/O (also via multivariable read/write)
Read/Write DB, Mk, IPI, IPQ, Timers, and Counters.

·         Directory
List Blocks, List Blocks of Type, Block info.

·         Control (1)
Run/Stop, Compress and Copy Ram to Rom.

·         Date and Time (2)
Get/Set PLC Date and Time.

·         System Info
Read SZL

·         Security
Get/Set session password (3).

Some functions exist only to simulate a PLC presence, particularly :

(1)Run command is accepted and subsequent get status command will show the CPU as running, Stop command is accepted and subsequent get status command will show the CPU as stopped. But they have no practical effect on the server.
Compress and Copy Ram to Rom are accepted but (obviously) they do nothing.

(2)Get Date and time returns the Host (PC in which the server is running) date and time. Set date and time is accepted but the host date and time is not modified.

(3)Whatever password is accepted.

 

(*) The maximum number of open TCP connections depends also on the Host OS.

S7 functions not implemented (in the current release)

·         Block Upload/Download
Is accepted but the server replies that the operation cannot be accomplished because the security level is not met : we cannot download a block, a block must be created by the host application then shared with the server.

·         Programming functions
The server does not replies at all.

·         Cyclic data I/O
The server does not replies at all.

 

Due to limit the memory footprint (the server must work fine into a Raspberry PI too), SDBs are not present; there is no scada, AFIK, that needs to access them.

 

Control flow

If you look at the previous small program, you see how to share resources between your application and the server.

We understood that the server replies automatically to the client requests, but :

There is a way to know what a client is requesting? Can we synchronize with it?

Is implemented a kind of log/debug mechanism?

Every time something happens in the server : when it is started, when it is stopped, when a client connects/disconnects or makes a request, an “event” is created.

The event is simply a struct defined as follow:

typedef struct{

    time_t EvtTime;    // Timestamp

    int EvtSender;     // Sender

    longword EvtCode// Event code

    word EvtRetCode;   // Event result

    word EvtParam1;    // Param 1 (if available)

    word EvtParam2;    // Param 2 (if available)

    word EvtParam3;    // Param 3 (if available)

    word EvtParam4;    // Param 4 (if available)

}TSrvEvent

 

 

 

 

 

 

 

EvtTime is the timestamp of the event, i.e. the date and time of its creation.

EvtSender is the IP of the Client involved in this event. The format is 32 bit integer to save memory, and can be converted into string, such as “192.168.0.34”, using the socket function inet_ntoa() (Every OS socket layer has it).
If the event sender is the server itself (event generated on its startup for example), this value is 0.

EvtCode is the Event code, i.e. its identifier (see the list below).

EvtRetCode is the Event Result, it coincides with the result of the underlying S7 function if any, otherwise is 0.

EvtParam1..EvtParam4 are parameters whose meaning depends on the context.

In snap_tcpsrv.h and s7_types.h you will find all constants used.

EvtCode List

const longword evcServerStarted       = 0x00000001;

const longword evcServerStopped       = 0x00000002;

const longword evcListenerCannotStart = 0x00000004;

const longword evcClientAdded         = 0x00000008;

const longword evcClientRejected      = 0x00000010;

const longword evcClientNoRoom        = 0x00000020;

const longword evcClientException     = 0x00000040;

const longword evcClientDisconnected  = 0x00000080;

const longword evcClientTerminated    = 0x00000100;

const longword evcClientsDropped      = 0x00000200;

const longword evcReserved_00000400   = 0x00000400;

const longword evcReserved_00000800   = 0x00000800;

const longword evcReserved_00001000   = 0x00001000;

const longword evcReserved_00002000   = 0x00002000;

const longword evcReserved_00004000   = 0x00004000;

const longword evcReserved_00008000   = 0x00008000;

const longword evcPDUincoming         = 0x00010000;

const longword evcDataRead            = 0x00020000;

const longword evcDataWrite           = 0x00040000;

const longword evcNegotiatePDU        = 0x00080000;

const longword evcReadSZL             = 0x00100000;

const longword evcClock               = 0x00200000;

const longword evcUpload              = 0x00400000;

const longword evcDownload            = 0x00800000;

const longword evcDirectory           = 0x01000000;

const longword evcSecurity            = 0x02000000;

const longword evcControl             = 0x04000000;

const longword evcReserved_08000000   = 0x08000000;

const longword evcReserved_10000000   = 0x10000000;

const longword evcReserved_20000000   = 0x20000000;

const longword evcReserved_40000000   = 0x40000000;

const longword evcReserved_80000000   = 0x80000000;

 

 

 

 

The event generated follows 2 ways : the events queue and the callbacks

The Events queue is a FIFO list protected with a critical section to ensure events consistency and it’s thread-safe.

 

Each S7 Worker inserts its events into the queue, your application extracts them using Srv_PickEvent().

If the queue is full, i.e. you don’t call Srv_PickEvent or call it too slowly, the event is not inserted and it’s simply discarded.

 

On calling Srv_ClearEvents() the queue is flushed.

 

The Event queue is designed for log purpose.

 

The next code snippet is extracted from ServerDemo (Pascal rich-demo).

-      LogTimer is a cyclic timer procedure.

-      Log is a Text Memo object.

 

 

procedure TFrmServer.LogTimer(Sender: TObject);

Var

  Event : TSrvEvent;

begin

  // Updates Log memo

  if Server.PickEvent(Event) then

  begin

    if Log.Lines.Count>1024 then // to limit the size

      Log.Lines.Clear;

    Log.Lines.Append(SrvEventText(Event));

  end;

  // Updates other Server Infos

  ServerStatus:=Server.ServerStatus;

  ClientsCount:=Server.ClientsCount;

end;

 

 

 

 

 

 

 

 

 

Finally, SrvEventText() returns the textual string of the event.

 

This is a sample output of this function:

 

2013-06-25 15:39:11 Server started

2013-06-25 15:39:24 [192.168.0.70] Client added

2013-06-25 15:39:24 [192.168.0.70] The client requires a PDU size of 480 bytes

2013-06-25 15:39:24 [192.168.0.70] Read SZL request, ID:0x0132 INDEX:0x0004 --> OK

2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0000 INDEX:0x0000 --> OK

2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0111 INDEX:0x0001 --> OK

2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0424 INDEX:0x0000 --> OK

2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0f74 INDEX:0x0000 --> OK

2013-06-25 15:39:25 [192.168.0.70] Read SZL request, ID:0x0074 INDEX:0x0000 --> OK

 

Ok, but I do not want to be bored with messages about SZL or Date/Time, can I filter them ?

 

Yes, you can filter them checking the EvtCode parameter but, even better, the server can make this for you.

 

If you look at the EvtCode List, you will notice that each event occupies one bit into a 32 bit word.

It’s possible to pass to the server a BitMask word, named LogMask, whose bits will act as “and gate” for the events.

 

For example, this mask 0xFFFFFFFE has the first bit set to 0.

If you pass it to the server, every event except for “Server Started” will be stored.

 

 

Callbacks

While, as said, the Event queue is designed for log purpose, the callback mechanism is designed for control purpose.

Also the callback mechanism is filtered with its own bitmask and is protected with a critical section.

There are two differents callbacks (from Snap7 1.1.0) the Read Callback and the Common Callback.

Both callbacks are executed in the same thread of the S7 Worker. The first is invoked on read request before performing the data transfer from Snap7Server to the Client, the second after the handshake with the client to avoid Client timeout since the code inside the callback locks the worker.
See Srv_SetReadEventsCallback() and Srv_SetEventsCallback() for further information about the callback prototype.

Log Mask and Callback mask are different, by default are set both to 0xFFFFFFFF (all enabled) on the server creation.

The purpose of the ReadEventCallback is for writing protocol converters or gateways.

Finally let’s see the complete sequence.

 

 

The Client Requests to read some data from DB4.

The Worker:

Invokes the Read Callback (if assigned) passing it the read coordinates.
Into the Read Callback we are able to modify DB4 if we want.

Gets the Data from DB4.

Sends the Data and Job result to the client.

Checks the Log Filter and inserts the Event into the queue.

Checks the Callback filter and, if the callback is assigned, calls the user function passing the event as parameter.

Becomes ready for further Client requests.

 

Remarks

 

Data consistency

Since the main application shares its resources with the server, a synchronization method is needed to ensure the data consistency.

When a memory block is shared via Srv_RegisterArea(), the server creates a block descriptor.

This descriptor contains

·         Block number (it’s used only if the block is a DB).

·         Block memory address.

·         Block size.

·         A CriticalSection Object reference.

Just that object ensures the data consistency.

The S7 worker “locks” the memory block every time it needs to access it, and unlocks it at the end.

To improve the performances, a double-buffer method is used: the S7 worker first receives the data into an internal buffer and then copies the content into the shared block.
Or, it copies the needed data from the shared block into the internal buffer before sending them.

Only the copy operation locks the block.

If you need data consistency, you must accomplish this rule.

For this purpose there are two functions : Srv_LockArea() and Srv_UnlockArea().

You should use the first one to lock a memory block and the second one to unlock it.

On long operations I suggest you to adopt the same double-buffer strategy : use an internal buffer then transfer the data into the shared block.
Moreover, an exception raised when the block is locked will lead to a S7 worker freeze.

Note

The granularity of the consistency is the PDU size.

 

Multiple servers

In preparation to receive connections a socket must be bound to a 2-tuple value :
(IP Address, Listening Port).

These coordinates are unique.

You can have a Telnet Server, a HTTP Server and a NTP Server running in the same machine because, though they have the same IP, they are listening on different ports.

You can have two HTTP Servers in the same machine that has two network adapters (i.e. two different IP addresses).

Established this (Berkley Sockets) rule, you can create multiple Snap7Servers but each of them must be “started” onto a different network adapter, because the listening port (ISO TCP - 102) cannot be changed.

It could be useful run two servers to share data between two different network segments.

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image011.png

If you plan to use a physical server, 16 adapters is the maximum suggested.
If you need more, consider to use a virtual infrastructure.

 

Troubleshooting

There are mainly three reasons for which a start error is generated:

The first, trivial, is that the bind address is wrong.

 

Windows

In the PC in which you are trying to start the server is installed the Step 7 / TIA Portal environment.

Step 7 has a server listening onto the port 102 : s7oiehsx.

To overcome this problem you can temporary stop it, by running one of the batch files:

stop_s7oiehsx_x86.bat (32 bit) or

stop_s7oiehsx_x64.bat (64 bit)

To re-run the Step 7 service use their counterpart start_s7oiehsx_xxx.bat.

You can find them in the examples folder.

Tanks to www.runmode.com

 

Unix

ISO TCP port is a well-known port, so in Unix you need root rights, or your application must have the SUID flag set, to bind a socket to this port.

There is not a workaround for this.

 

If a client does not connect with the server, check your Firewall settings (especially if the host OS is Windows 7 / Windows 8).


 

Step 7 Server project

This is a sample PG Project of the Snap7Server, with this project you can:

·         Connect the Step 7 Manager (or Tia Portal if you convert it) to the Snap7Server and see it online.

·         Insert it in a multi-cpu project.

·         Integrate a WinCC flexible project into it.

 

You can find it in the folder examples/Step7/Server

To use it you only need to setup the network IP address (as you would do, in a real project).

There are few steps to follow:

1) Load the project into Step 7 Simatic Manager.

2) Open the Hardware Configuration

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image012.png

3)  Open the PN-IO Interface editor

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image013.jpg


4) Open the Network properties editor

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image014.jpg

 

5) Set the IP Address of the PC in which the server will run (and confirm pressing the OK Button).

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image015.png

 

 

6) Close all previous windows, Save & Compile the Project, don’t download it.

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image016.jpg

7) You can close the Hardware Configuration Editor, that’s all.

 

In the host PC, run a Server program, any of the ones you find in the example folder or the rich-demo ServerDemo if you want.

Now you can go ONLINE with Simatic Manager, remember to set TCP/IP Auto as PG interface.

 

Descrizione: C:\Snap7\Deploy-1.4.0\www 1.4\snap7_server_file\image017.png

 

You can modify the offline project adding DB, variables and so on, and link them to WinCC variables.
Remember, obviously, to create the same DBs into your application and to share them.

Remarks

Snap7Server is not visible via the “Display accessible nodes” function of Simatic manager, because to find the Ethernet nodes a Profinet packet (ServiceID=5, Discover, All) is used.

However, even WinAC, is visible with this method if it’s equipped only with IE (Industrial Ethernet) standard adapter.

 


 

Server Applications

The final 1.000.000 € (Eur. because I’m Italian) question is:

Why do I need this Object ?

 

Let’s see two real scenarios:

 

Using PLC-aware hardware with your software

You have PC-based automation but:

o   You don’t want to create graphic screens.

o   You don't want to “expose” the system with a standard keyboard.

With Snap7Server you can connect an HMI-Panel (Siemens/ESA/Pro-Face/Open source Scada) to your application and use a standard HMI-Builder.

Tomorrow you can migrate your application to another OS without be bored by graphic libraries porting.

Variant of the above:

You have a Raspberry-based (or other small Linux card) system and:

o   You don’t want to use an HDMI monitor.

o   And you don’t like a sad SSH interface.

o   You like a small ISO-rail mounted box, wired using only two cables (network and power).

Finally:

Snap7Server allows your embedded hardware to be connected by hundred standard systems Siemens-Aware.

 

Integration in a PLC environment.

You build an analogic test bench to be inserted in an existing PLC-based production line and sadly realize that:

o   Your bench is PC-based.

o   The line has a commercial scada as supervisor.

o   You don’t want to buy a PLC as line interface layer.

The commercial scada can be smoothly interfaced with your application via a Snap7Server (moreover this solution is faster).