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.
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
Module information
Communication Info
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.
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.
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.
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
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.
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.
If you plan to use a physical server, 16
adapters is the maximum suggested.
If you need more, consider to use a virtual infrastructure.
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).
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
3) Open the PN-IO Interface editor
4) Open
the Network properties editor
5) Set the IP Address of the PC in which the
server will run (and
confirm pressing the OK Button).
6) Close all previous windows, Save &
Compile the Project, don’t download it.
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.
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.
The final 1.000.000 € (Eur. because I’m
Italian) question is:
Why do I need this Object ?
Let’s see two real scenarios:
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.
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).