@@ -0,0 +1,58 @@
+*.diff
+*.patch
+*.orig
+*.rej
+
+*~
+.#*
+*#
+*.flt
+*.gmo
+*.info
+*.la
+*.lo
+*.o
+*.pyc
+*.tmp
+.deps
+.libs
+autom4te.cache
+config.cache
+config.h
+config.intl
+config.log
+config.status
+libtool
+POTFILES
+*-POTFILES
+TAGS
+TAGS.sub
+.local.vimrc
+.lvimrc
+.clang-format
+.gdbinit
+.gdb_history
+# ignore core files, but not java/net/protocol/core/
+core
+!core/
+lost+found
+# ignore ./contrib/gcc_update output
+LAST_UPDATED
+REVISION
+# ignore in-tree prerequisites
+/mpfr*
+/mpc*
+/gmp*
+/isl*
@@ -0,0 +1,9 @@
+---------------------------------------------
+Snap7 IoT Package for Intel Quark architecure
+Devices Tested
+-------------------------
+Intel Galileo Gen 2
+Siemens SIMATIC IOT2040
@@ -0,0 +1,7 @@
+Folders
+bin - Library output directory divided by platform-os
+temp - Intermediate objects/temp files, can be safety emptied...
+unix - Unix (Linux/BSD/Solaris) makefiles directory
+osx - OSX makefiles directory
+windows - Windows projects/makefiles directory divided by compilers
@@ -0,0 +1,2 @@
+These folders will contain binary libraries generated from project/makefiles.
@@ -0,0 +1 @@
+These folders contain intermediate objects/temp files, can be safety emptied...
@@ -0,0 +1,111 @@
+#
+# Common for every unix flavour (any changes will be reflected on all platforms)
+Platform :=$(TargetCPU)-$(OS)
+ConfigurationName :=Release
+IntermediateDirectory :=../temp/$(TargetCPU)
+OutDir := $(IntermediateDirectory)
+LinkerName :=g++
+SharedObjectLinkerName :=g++ -shared -fPIC
+DebugSwitch :=-gstab
+IncludeSwitch :=-I
+LibrarySwitch :=-l
+OutputSwitch :=-o
+LibraryPathSwitch :=-L
+PreprocessorSwitch :=-D
+SourceSwitch :=-c
+OutputFile :=../bin/$(Platform)/libsnap7.so
+PreprocessOnlySwitch :=-E
+ObjectsFileList :="filelist.txt"
+MakeDirCommand :=mkdir -p
+LinkOptions := -O3
+IncludePath := $(IncludeSwitch). $(IncludeSwitch)../../src/sys $(IncludeSwitch)../../src/core $(IncludeSwitch)../../src/lib
+Libs := $(LibrarySwitch)pthread $(LibrarySwitch)rt
+LibPath := $(LibraryPathSwitch).
+LibInstall := /usr/lib
+##
+## Common variables (CXXFLAGS varies across platforms)
+AR := ar rcus
+CXX := g++
+CC := gcc
+CFLAGS :=
+## User defined environment variables
+Objects0=$(IntermediateDirectory)/sys_snap_msgsock.o $(IntermediateDirectory)/sys_snap_sysutils.o $(IntermediateDirectory)/sys_snap_tcpsrvr.o $(IntermediateDirectory)/sys_snap_threads.o $(IntermediateDirectory)/core_s7_client.o $(IntermediateDirectory)/core_s7_isotcp.o $(IntermediateDirectory)/core_s7_partner.o $(IntermediateDirectory)/core_s7_peer.o $(IntermediateDirectory)/core_s7_server.o $(IntermediateDirectory)/core_s7_text.o \
+ $(IntermediateDirectory)/core_s7_micro_client.o $(IntermediateDirectory)/lib_snap7_libmain.o
+Objects=$(Objects0)
+## Main Build Targets
+.PHONY: all clean install PreBuild PostBuild
+all: $(OutputFile)
+$(OutputFile): $(IntermediateDirectory)/.d $(Objects)
+ @$(MakeDirCommand) $(@D)
+ @$(MakeDirCommand) $(IntermediateDirectory)
+ @echo $(Objects0) > $(ObjectsFileList)
+ $(SharedObjectLinkerName) $(OutputSwitch)$(OutputFile) @$(ObjectsFileList) $(LibPath) $(Libs) $(LinkOptions)
+ $(RM) $(ObjectsFileList)
+$(IntermediateDirectory)/.d:
+ @test -d ../temp/$(TargetCPU) || $(MakeDirCommand) ../temp/$(TargetCPU)
+PreBuild:
+PostBuild:
+## Objects
+$(IntermediateDirectory)/sys_snap_msgsock.o:
+ $(CXX) $(SourceSwitch) "../../src/sys/snap_msgsock.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_msgsock.o $(IncludePath)
+$(IntermediateDirectory)/sys_snap_sysutils.o:
+ $(CXX) $(SourceSwitch) "../../src/sys/snap_sysutils.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_sysutils.o $(IncludePath)
+$(IntermediateDirectory)/sys_snap_tcpsrvr.o:
+ $(CXX) $(SourceSwitch) "../../src/sys/snap_tcpsrvr.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_tcpsrvr.o $(IncludePath)
+$(IntermediateDirectory)/sys_snap_threads.o:
+ $(CXX) $(SourceSwitch) "../../src/sys/snap_threads.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/sys_snap_threads.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_client.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_client.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_client.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_isotcp.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_isotcp.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_isotcp.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_partner.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_partner.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_partner.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_peer.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_peer.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_peer.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_server.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_server.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_server.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_text.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_text.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_text.o $(IncludePath)
+$(IntermediateDirectory)/core_s7_micro_client.o:
+ $(CXX) $(SourceSwitch) "../../src/core/s7_micro_client.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/core_s7_micro_client.o $(IncludePath)
+$(IntermediateDirectory)/lib_snap7_libmain.o:
+ $(CXX) $(SourceSwitch) "../../src/lib/snap7_libmain.cpp" $(CXXFLAGS) -o $(IntermediateDirectory)/lib_snap7_libmain.o $(IncludePath)
+## Clean / Install
+clean:
+ $(RM) $(IntermediateDirectory)/*.o
+ $(RM) $(OutputFile)
+install: all
+ cp -f $(OutputFile) $(LibInstall)
@@ -0,0 +1,11 @@
+## i386 Linux based (Debian/Ubuntu/Red Hat/Slackware etc.) Makefile
+## Use make
+TargetCPU :=i586
+OS :=linux
+CXXFLAGS := -O3 -fPIC -pedantic
+# Standard part
+include common.mk
@@ -0,0 +1,120 @@
+/*=============================================================================|
+| PROJECT SNAP7 1.4.0 |
+|==============================================================================|
+| Copyright (C) 2013, 2014 Davide Nardella |
+| All rights reserved. |
+| SNAP7 is free software: you can redistribute it and/or modify |
+| it under the terms of the Lesser GNU General Public License as published by |
+| the Free Software Foundation, either version 3 of the License, or |
+| (at your option) any later version. |
+| |
+| It means that you can distribute your commercial software linked with |
+| SNAP7 without the requirement to distribute the source code of your |
+| application and without the requirement that your application be itself |
+| distributed under LGPL. |
+| SNAP7 is distributed in the hope that it will be useful, |
+| but WITHOUT ANY WARRANTY; without even the implied warranty of |
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+| Lesser GNU General Public License for more details. |
+| You should have received a copy of the GNU General Public License and a |
+| copy of Lesser GNU General Public License along with Snap7. |
+| If not, see http://www.gnu.org/licenses/ |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "snap7.h"
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+ int cnt = 0;
+ const int size = 256;
+ byte Buffer[size];
+ TS7Partner *Partner;
+//------------------------------------------------------------------------------
+// Usage syntax
+static void Usage()
+{
+ printf("Usage\n");
+ printf(" APartner <PassiveIP>\n");
+ printf("Where\n");
+ printf(" <PassiveIP> is the address of the passive partner that we want to connect.\n");
+ printf("Note\n");
+ printf("- Local Address is meaningless\n");
+ printf("- Both Local TSAP and Remote TSAP are set to 0x1002\n");
+ printf("- You can create multiple active partner in the same\n");
+ printf(" program or across different programs.\n");
+}
+// Simply fills the buffer with a progressive number
+void PrepareBuffer()
+ cnt++;
+ for (int i = 0; i < size; i++)
+ Buffer[i] = cnt;
+// SysSleep (copied from snap_sysutils.cpp)
+void SysSleep(longword Delay_ms)
+ Sleep(Delay_ms);
+#else
+ struct timespec ts;
+ ts.tv_sec = (time_t)(Delay_ms / 1000);
+ ts.tv_nsec =(long)((Delay_ms - ts.tv_sec) * 1000000);
+ nanosleep(&ts, (struct timespec *)0);
+// Main
+int main(int argc, char* argv[])
+ int SndError = 0;
+ // Get Progran args
+ if (argc!=2)
+ {
+ Usage();
+ return 1;
+ };
+ // Create the ACTIVE partner
+ Partner = new TS7Partner(true);
+ // Start
+ // Local Address for an active partner is meaningless, leave
+ // it always set to "0.0.0.0"
+ int Error=Partner->StartTo("0.0.0.0", argv[1], 0x1002, 0x1002);
+ if (Error != 0)
+ printf("%s\n",ParErrorText(Error).c_str());
+ }
+ // Endless loop : Exit with Ctrl-C
+ while (true)
+ while (!Partner->Linked())
+ printf("Connecting to %s ...\n",argv[1]);
+ SysSleep(500);
+ do
+ PrepareBuffer();
+ SndError = Partner->BSend(0x00000001, &Buffer, size);
+ if (SndError == 0)
+ printf("Succesfully sent %d bytes\n",size);
+ else
+ printf("%s\n",ParErrorText(SndError).c_str());
+ } while (SndError == 0);
@@ -0,0 +1,515 @@
+| Client Example |
+ TS7Client *Client;
+ byte Buffer[65536]; // 64 K buffer
+ int SampleDBNum = 1000;
+ char *Address; // PLC IP Address
+ int Rack=0,Slot=2; // Default Rack and Slot
+ int ok = 0; // Number of test pass
+ int ko = 0; // Number of test failure
+ bool JobDone=false;
+ int JobResult=0;
+// Async completion callback
+// This is a simply text demo, we use callback only to set an internal flag...
+void S7API CliCompletion(void *usrPtr, int opCode, int opResult)
+ JobResult=opResult;
+ JobDone = true;
+// SysSleep (copied from snap_sysutils.cpp) multiplatform millisec sleep
+// Usage Syntax
+void Usage()
+ printf(" client <IP> [Rack=0 Slot=2]\n");
+ printf("Example\n");
+ printf(" client 192.168.1.101 0 2\n");
+ printf("or\n");
+ printf(" client 192.168.1.101\n");
+ getchar();
+// hexdump, a very nice function, it's not mine.
+// I found it on the net somewhere some time ago... thanks to the author ;-)
+#ifndef HEXDUMP_COLS
+#define HEXDUMP_COLS 16
+void hexdump(void *mem, unsigned int len)
+ unsigned int i, j;
+ for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
+ /* print offset */
+ if(i % HEXDUMP_COLS == 0)
+ printf("0x%04x: ", i);
+ /* print hex data */
+ if(i < len)
+ printf("%02x ", 0xFF & ((char*)mem)[i]);
+ else /* end of block, just aligning for ASCII dump */
+ printf(" ");
+ /* print ASCII dump */
+ if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
+ for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
+ if(j >= len) /* end of block, not really printing */
+ putchar(' ');
+ else if(isprint((((char*)mem)[j] & 0x7F))) /* printable char */
+ putchar(0xFF & ((char*)mem)[j]);
+ else /* other char */
+ putchar('.');
+ putchar('\n');
+// Check error
+bool Check(int Result, const char * function)
+ printf("\n");
+ printf("+-----------------------------------------------------\n");
+ printf("| %s\n",function);
+ if (Result==0) {
+ printf("| Result : OK\n");
+ printf("| Execution time : %d ms\n",Client->ExecTime());
+ ok++;
+ else {
+ printf("| ERROR !!! \n");
+ if (Result<0)
+ printf("| Library Error (-1)\n");
+ printf("| %s\n",CliErrorText(Result).c_str());
+ ko++;
+ return Result==0;
+// Multi Read
+void MultiRead()
+ // Multiread buffers
+ byte MB[16]; // 16 Merker bytes
+ byte EB[16]; // 16 Digital Input bytes
+ byte AB[16]; // 16 Digital Output bytes
+ word TM[8]; // 8 timers
+ word CT[8]; // 8 counters
+ // Prepare struct
+ TS7DataItem Items[5];
+ // NOTE : *AMOUNT IS NOT SIZE* , it's the number of items
+ // Merkers
+ Items[0].Area =S7AreaMK;
+ Items[0].WordLen =S7WLByte;
+ Items[0].DBNumber =0; // Don't need DB
+ Items[0].Start =0; // Starting from 0
+ Items[0].Amount =16; // 16 Items (bytes)
+ Items[0].pdata =&MB;
+ // Digital Input bytes
+ Items[1].Area =S7AreaPE;
+ Items[1].WordLen =S7WLByte;
+ Items[1].DBNumber =0; // Don't need DB
+ Items[1].Start =0; // Starting from 0
+ Items[1].Amount =16; // 16 Items (bytes)
+ Items[1].pdata =&EB;
+ // Digital Output bytes
+ Items[2].Area =S7AreaPA;
+ Items[2].WordLen =S7WLByte;
+ Items[2].DBNumber =0; // Don't need DB
+ Items[2].Start =0; // Starting from 0
+ Items[2].Amount =16; // 16 Items (bytes)
+ Items[2].pdata =&AB;
+ // Timers
+ Items[3].Area =S7AreaTM;
+ Items[3].WordLen =S7WLTimer;
+ Items[3].DBNumber =0; // Don't need DB
+ Items[3].Start =0; // Starting from 0
+ Items[3].Amount =8; // 8 Timers
+ Items[3].pdata =&TM;
+ // Counters
+ Items[4].Area =S7AreaCT;
+ Items[4].WordLen =S7WLCounter;
+ Items[4].DBNumber =0; // Don't need DB
+ Items[4].Start =0; // Starting from 0
+ Items[4].Amount =8; // 8 Counters
+ Items[4].pdata =&CT;
+ int res=Client->ReadMultiVars(&Items[0],5);
+ if (Check(res,"Multiread Vars"))
+ // Result of Client->ReadMultivars is the "global result" of
+ // the function, it's OK if something was exchanged.
+ // But we need to check single Var results.
+ // Let shall suppose that we ask for 5 vars, 4 of them are ok but
+ // the 5th is inexistent, we will have 4 results ok and 1 not ok.
+ printf("Dump MB0..MB15 - Var Result : %d\n",Items[0].Result);
+ if (Items[0].Result==0)
+ hexdump(&MB,16);
+ printf("Dump EB0..EB15 - Var Result : %d\n",Items[1].Result);
+ if (Items[1].Result==0)
+ hexdump(&EB,16);
+ printf("Dump AB0..AB15 - Var Result : %d\n",Items[2].Result);
+ if (Items[2].Result==0)
+ hexdump(&AB,16);
+ printf("Dump T0..T7 - Var Result : %d\n",Items[3].Result);
+ if (Items[3].Result==0)
+ hexdump(&TM,16); // 8 Timers -> 16 bytes
+ printf("Dump Z0..Z7 - Var Result : %d\n",Items[4].Result);
+ if (Items[4].Result==0)
+ hexdump(&CT,16); // 8 Counters -> 16 bytes
+// List blocks in AG
+void ListBlocks()
+ TS7BlocksList List;
+ int res=Client->ListBlocks(&List);
+ if (Check(res,"List Blocks in AG"))
+ printf(" OBCount : %d\n",List.OBCount);
+ printf(" FBCount : %d\n",List.FBCount);
+ printf(" FCCount : %d\n",List.FCCount);
+ printf(" SFBCount : %d\n",List.SFBCount);
+ printf(" SFCCount : %d\n",List.SFCCount);
+ printf(" DBCount : %d\n",List.DBCount);
+ printf(" SDBCount : %d\n",List.SDBCount);
+// CPU Info : catalog
+void OrderCode()
+ TS7OrderCode Info;
+ int res=Client->GetOrderCode(&Info);
+ if (Check(res,"Catalog"))
+ printf(" Order Code : %s\n",Info.Code);
+ printf(" Version : %d.%d.%d\n",Info.V1,Info.V2,Info.V3);
+// CPU Info : unit info
+void CpuInfo()
+ TS7CpuInfo Info;
+ int res=Client->GetCpuInfo(&Info);
+ if (Check(res,"Unit Info"))
+ printf(" Module Type Name : %s\n",Info.ModuleTypeName);
+ printf(" Serial Number : %s\n",Info.SerialNumber);
+ printf(" AS Name : %s\n",Info.ASName);
+ printf(" Module Name : %s\n",Info.ModuleName);
+// CP Info
+void CpInfo()
+ TS7CpInfo Info;
+ int res=Client->GetCpInfo(&Info);
+ if (Check(res,"Communication processor Info"))
+ printf(" Max PDU Length : %d bytes\n",Info.MaxPduLengt);
+ printf(" Max Connections : %d \n",Info.MaxConnections);
+ printf(" Max MPI Rate : %d bps\n",Info.MaxMpiRate);
+ printf(" Max Bus Rate : %d bps\n",Info.MaxBusRate);
+// PLC Status
+void UnitStatus()
+ int res=0;
+ int Status=Client->PlcStatus();
+ if (Check(res,"CPU Status"))
+ switch (Status)
+ case S7CpuStatusRun : printf(" RUN\n"); break;
+ case S7CpuStatusStop: printf(" STOP\n"); break;
+ default : printf(" UNKNOWN\n"); break;
+// Upload DB0 (surely exists in AG)
+void UploadDB0()
+ int Size = sizeof(Buffer); // Size is IN/OUT par
+ // In input it tells the client the size available
+ // In output it tells us how many bytes were uploaded.
+ int res=Client->Upload(Block_SDB, 0, &Buffer, &Size);
+ if (Check(res,"Block Upload (SDB 0)"))
+ printf("Dump (%d bytes) :\n",Size);
+ hexdump(&Buffer,Size);
+// Async Upload DB0 (using callback as completion trigger)
+void AsCBUploadDB0()
+ JobDone=false;
+ int res=Client->AsUpload(Block_SDB, 0, &Buffer, &Size);
+ if (res==0)
+ while (!JobDone)
+ SysSleep(100);
+ res=JobResult;
+ if (Check(res,"Async (callback) Block Upload (SDB 0)"))
+// Async Upload DB0 (using event wait as completion trigger)
+void AsEWUploadDB0()
+ res=Client->WaitAsCompletion(3000);
+ if (Check(res,"Async (Wait event) Block Upload (SDB 0)"))
+// Async Upload DB0 (using polling as completion trigger)
+void AsPOUploadDB0()
+ while (!Client->CheckAsCompletion(&res))
+ if (Check(res,"Async (polling) Block Upload (SDB 0)"))
+// Read a sample SZL Block
+void ReadSzl_0011_0000()
+ PS7SZL SZL = PS7SZL(&Buffer); // use our buffer casted as TS7SZL
+ int Size = sizeof(Buffer);
+ // Block ID 0x0011 IDX 0x0000 normally exists in every CPU
+ int res=Client->ReadSZL(0x0011, 0x0000, SZL, &Size);
+ if (Check(res,"Read SZL - ID : 0x0011, IDX 0x0000"))
+ printf(" LENTHDR : %d\n",SZL->Header.LENTHDR);
+ printf(" N_DR : %d\n",SZL->Header.N_DR);
+// Unit Connection
+bool CliConnect()
+ int res = Client->ConnectTo(Address,Rack,Slot);
+ if (Check(res,"UNIT Connection")) {
+ printf(" Connected to : %s (Rack=%d, Slot=%d)\n",Address,Rack,Slot);
+ printf(" PDU Requested : %d bytes\n",Client->PDURequested());
+ printf(" PDU Negotiated : %d bytes\n",Client->PDULength());
+ return res==0;
+// Unit Disconnection
+void CliDisconnect()
+ Client->Disconnect();
+// Perform readonly tests, no cpu status modification
+void PerformTests()
+ OrderCode();
+ CpuInfo();
+ CpInfo();
+ UnitStatus();
+ ReadSzl_0011_0000();
+ UploadDB0();
+ AsCBUploadDB0();
+ AsEWUploadDB0();
+ AsPOUploadDB0();
+ MultiRead();
+// Tests Summary
+void Summary()
+ printf("| Test Summary \n");
+ printf("| Performed : %d\n",(ok+ko));
+ printf("| Passed : %d\n",ok);
+ printf("| Failed : %d\n",ko);
+ printf("+----------------------------------------[press a key]\n");
+// Get Progran args (we need the client address and optionally Rack and Slot)
+ if (argc!=2 && argc!=4)
+ Address=argv[1];
+ if (argc==4)
+ Rack=atoi(argv[2]);
+ Slot=atoi(argv[3]);
+// Client Creation
+ Client= new TS7Client();
+ Client->SetAsCallback(CliCompletion,NULL);
+// Connection
+ if (CliConnect())
+ PerformTests();
+ CliDisconnect();
+// Deletion
+ delete Client;
+ Summary();
+ return 0;
@@ -0,0 +1,33 @@
+## LINUX barebone makefile for c++ examples : good for all platforms
+## Simply run make or make clean
+## Intend this makefile only as a "batch examples updater" after library modification.
+Libs := -lsnap7
+Wrapper :=snap7.cpp
+CXXFLAGS :=-O3
+.PHONY: all clean
+all:
+ $(CXX) $(CXXFLAGS) -o client ../client.cpp ../$(Wrapper) $(Libs)
+ $(CXX) $(CXXFLAGS) -o server ../server.cpp ../$(Wrapper) $(Libs)
+ $(CXX) $(CXXFLAGS) -o server_v10 ../server_v10.cpp ../$(Wrapper) $(Libs)
+ $(CXX) $(CXXFLAGS) -o srv_resourceless ../srv_resourceless.cpp ../$(Wrapper) $(Libs)
+ $(CXX) $(CXXFLAGS) -o apartner ../apartner.cpp ../$(Wrapper) $(Libs)
+ $(CXX) $(CXXFLAGS) -o ppartner ../ppartner.cpp ../$(Wrapper) $(Libs)
+ $(RM) client
+ $(RM) server
+ $(RM) server_v10
+ $(RM) srv_resourceless
+ $(RM) apartner
+ $(RM) ppartner
@@ -0,0 +1,165 @@
+| Passive Partner Example |
+ printf(" PPartner <ActiveIP>\n");
+ printf(" <ActiveIP> is the address of the active partner that we are waiting for.\n");
+ printf("- Local Address is set to 0.0.0.0 (the default adapter)\n");
+ printf("- You can create multiple passive partners bound to the same\n");
+ printf(" local address in the same program, but you cannot execute\n");
+ printf(" multiple instance of this program.\n");
+// Callback on data ready
+void S7API RecvCallback(void * usrPtr, int opResult, longword R_ID, void *pdata, int Size)
+ printf("R_ID : %d\n",R_ID);
+ printf("Size : %d\n",Size);
+ hexdump(pdata, Size);
+ if (argc != 2)
+ // Create the PASSIVE partner
+ Partner = new TS7Partner(false);
+ // Set the BRecv callback
+ Partner->SetRecvCallback(RecvCallback, NULL);
+ if (Error == 0)
+ printf("Passive partner started\n");
+ // If you got a start error:
+ // Windows - most likely you ar running the server in a pc on wich is
+ // installed step 7 : open a command prompt and type
+ // "net stop s7oiehsx" (Win32) or
+ // "net stop s7oiehsx64" (Win64)
+ // And after this test :
+ // "net start s7oiehsx" (Win32) or
+ // "net start s7oiehsx64" (Win64)
+ // Unix - you need root rights :-( because the isotcp port (102) is
+ // low and so it's considered "privileged".
+ Partner->Stop();
+ delete Partner;
@@ -0,0 +1,130 @@
+| New Server Example (1.1.0) |
+| Here we set ReadEventCallback to get in advance which area the client needs |
+| then we fill this area with a counter. |
+| The purpose is to show how to modify an area before it be trasferred to the |
+| client |
+#include <cstring>
+ TS7Server *Server;
+ unsigned char DB21[512]; // Our DB1
+ unsigned char DB103[1280]; // Our DB2
+ unsigned char DB3[1024]; // Our DB3
+ byte cnt = 0;
+// Here we use the callback to show the log, this is not the best choice since
+// the callback is synchronous with the client access, i.e. the server cannot
+// handle futher request from that client until the callback is complete.
+// The right choice is to use the log queue via the method PickEvent.
+void S7API EventCallBack(void *usrPtr, PSrvEvent PEvent, int Size)
+ // print the event
+ printf("%s\n",SrvEventText(PEvent).c_str());
+};
+// The read event callback is called multiple times in presence of multiread var request
+void S7API ReadEventCallBack(void *usrPtr, PSrvEvent PEvent, int Size)
+ // print the read event
+ if (PEvent->EvtParam1==S7AreaDB)
+ // As example the DB requested is filled before transferred
+ // EvtParam1 contains the DB number.
+ switch (PEvent->EvtParam2)
+ case 1 : memset(&DB21, ++cnt, sizeof(DB21));break;
+ case 2 : memset(&DB103, ++cnt, sizeof(DB103));break;
+ case 3 : memset(&DB3, ++cnt, sizeof(DB3));break;
+ int Error;
+ Server = new TS7Server;
+ // Share some resources with our virtual PLC
+ Server->RegisterArea(srvAreaDB, // We are registering a DB
+ 21, // Its number is 1 (DB1)
+ &DB21, // Our buffer for DB1
+ sizeof(DB21)); // Its size
+ // Do the same for DB2 and DB3
+ Server->RegisterArea(srvAreaDB, 103, &DB103, sizeof(DB103));
+ Server->RegisterArea(srvAreaDB, 3, &DB3, sizeof(DB3));
+ // We mask the read event to avoid the double trigger for the same event
+ Server->SetEventsMask(~evcDataRead);
+ Server->SetEventsCallback(EventCallBack, NULL);
+ // Set the Read Callback
+ Server->SetReadEventsCallback(ReadEventCallBack, NULL);
+ // Start the server onto the default adapter.
+ // To select an adapter we have to use Server->StartTo("192.168.x.y").
+ // Start() is the same of StartTo("0.0.0.0");
+ Error=Server->Start();
+ if (Error==0)
+ // Now the server is running ... wait a key to terminate
+ printf("%s\n",SrvErrorText(Error).c_str());
+ Server->Stop(); // <- not strictly needed, every server is stopped on deletion
+ // and every client is gracefully disconnected.
+ delete Server;
+// Finally, this is a very minimalist (but working) server :
+/*
+ TS7Server *Server = new TS7Server;
+ Server->Start();
+*/
@@ -0,0 +1,106 @@
+| PROJECT SNAP7 1.0.0 |
+| Copyright (C) 2013, Davide Nardella |
+| Server Example |
+ unsigned char DB1[512]; // Our DB1
+ unsigned char DB2[128]; // Our DB2
+ 1, // Its number is 1 (DB1)
+ &DB1, // Our buffer for DB1
+ sizeof(DB1)); // Its size
+ Server->RegisterArea(srvAreaDB, 2, &DB2, sizeof(DB2));
+ // Set the event callback to show something : it's not strictly needed.
+ // If you comment next line the server still works fine.
@@ -0,0 +1,775 @@
+| C++ Snap 7 classes Implementation |
+//==============================================================================
+// CLIENT
+TS7Client::TS7Client()
+ Client=Cli_Create();
+//---------------------------------------------------------------------------
+TS7Client::~TS7Client()
+ Cli_Destroy(&Client);
+int TS7Client::Connect()
+ return Cli_Connect(Client);
+int TS7Client::ConnectTo(const char *RemAddress, int Rack, int Slot)
+ return Cli_ConnectTo(Client, RemAddress, Rack, Slot);
+int TS7Client::SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTSAP)
+ return Cli_SetConnectionParams(Client, RemAddress, LocalTSAP, RemoteTSAP);
+int TS7Client::SetConnectionType(word ConnectionType)
+ return Cli_SetConnectionType(Client, ConnectionType);
+int TS7Client::Disconnect()
+ return Cli_Disconnect(Client);
+int TS7Client::GetParam(int ParamNumber, void *pValue)
+ return Cli_GetParam(Client, ParamNumber, pValue);
+int TS7Client::SetParam(int ParamNumber, void *pValue)
+ return Cli_SetParam(Client, ParamNumber, pValue);
+int TS7Client::ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return Cli_ReadArea(Client, Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int TS7Client::WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return Cli_WriteArea(Client, Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int TS7Client::ReadMultiVars(PS7DataItem Item, int ItemsCount)
+ return Cli_ReadMultiVars(Client, Item, ItemsCount);
+int TS7Client::WriteMultiVars(PS7DataItem Item, int ItemsCount)
+ return Cli_WriteMultiVars(Client, Item, ItemsCount);
+int TS7Client::DBRead(int DBNumber, int Start, int Size, void *pUsrData)
+ return Cli_DBRead(Client, DBNumber, Start, Size, pUsrData);
+int TS7Client::DBWrite(int DBNumber, int Start, int Size, void *pUsrData)
+ return Cli_DBWrite(Client, DBNumber, Start, Size, pUsrData);
+int TS7Client::MBRead(int Start, int Size, void *pUsrData)
+ return Cli_MBRead(Client, Start, Size, pUsrData);
+int TS7Client::MBWrite(int Start, int Size, void *pUsrData)
+ return Cli_MBWrite(Client, Start, Size, pUsrData);
+int TS7Client::EBRead(int Start, int Size, void *pUsrData)
+ return Cli_EBRead(Client, Start, Size, pUsrData);
+int TS7Client::EBWrite(int Start, int Size, void *pUsrData)
+ return Cli_EBWrite(Client, Start, Size, pUsrData);
+int TS7Client::ABRead(int Start, int Size, void *pUsrData)
+ return Cli_ABRead(Client, Start, Size, pUsrData);
+int TS7Client::ABWrite(int Start, int Size, void *pUsrData)
+ return Cli_ABWrite(Client, Start, Size, pUsrData);
+int TS7Client::TMRead(int Start, int Amount, void *pUsrData)
+ return Cli_TMRead(Client, Start, Amount, pUsrData);
+int TS7Client::TMWrite(int Start, int Amount, void *pUsrData)
+ return Cli_TMWrite(Client, Start, Amount, pUsrData);
+int TS7Client::CTRead(int Start, int Amount, void *pUsrData)
+ return Cli_CTRead(Client, Start, Amount, pUsrData);
+int TS7Client::CTWrite(int Start, int Amount, void *pUsrData)
+ return Cli_CTWrite(Client, Start, Amount, pUsrData);
+int TS7Client::ListBlocks(PS7BlocksList pUsrData)
+ return Cli_ListBlocks(Client, pUsrData);
+int TS7Client::GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData)
+ return Cli_GetAgBlockInfo(Client, BlockType, BlockNum, pUsrData);
+int TS7Client::GetPgBlockInfo(void *pBlock, PS7BlockInfo pUsrData, int Size)
+ return Cli_GetPgBlockInfo(Client, pBlock, pUsrData, Size);
+int TS7Client::ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount)
+ return Cli_ListBlocksOfType(Client, BlockType, pUsrData, ItemsCount);
+int TS7Client::Upload(int BlockType, int BlockNum, void *pUsrData, int *Size)
+ return Cli_Upload(Client, BlockType, BlockNum, pUsrData, Size);
+int TS7Client::FullUpload(int BlockType, int BlockNum, void *pUsrData, int *Size)
+ return Cli_FullUpload(Client, BlockType, BlockNum, pUsrData, Size);
+int TS7Client::Download(int BlockNum, void *pUsrData, int Size)
+ return Cli_Download(Client, BlockNum, pUsrData, Size);
+int TS7Client::Delete(int BlockType, int BlockNum)
+ return Cli_Delete(Client, BlockType, BlockNum);
+int TS7Client::DBGet(int DBNumber, void *pUsrData, int *Size)
+ return Cli_DBGet(Client, DBNumber, pUsrData, Size);
+int TS7Client::DBFill(int DBNumber, int FillChar)
+ return Cli_DBFill(Client, DBNumber, FillChar);
+int TS7Client::GetPlcDateTime(tm *DateTime)
+ return Cli_GetPlcDateTime(Client, DateTime);
+int TS7Client::SetPlcDateTime(tm *DateTime)
+ return Cli_SetPlcDateTime(Client, DateTime);
+int TS7Client::SetPlcSystemDateTime()
+ return Cli_SetPlcSystemDateTime(Client);
+int TS7Client::GetOrderCode(PS7OrderCode pUsrData)
+ return Cli_GetOrderCode(Client, pUsrData);
+int TS7Client::GetCpuInfo(PS7CpuInfo pUsrData)
+ return Cli_GetCpuInfo(Client, pUsrData);
+int TS7Client::GetCpInfo(PS7CpInfo pUsrData)
+ return Cli_GetCpInfo(Client, pUsrData);
+int TS7Client::ReadSZL(int ID, int Index, PS7SZL pUsrData, int *Size)
+ return Cli_ReadSZL(Client, ID, Index, pUsrData, Size);
+int TS7Client::ReadSZLList(PS7SZLList pUsrData, int *ItemsCount)
+ return Cli_ReadSZLList(Client, pUsrData, ItemsCount);
+int TS7Client::PlcHotStart()
+ return Cli_PlcHotStart(Client);
+int TS7Client::PlcColdStart()
+ return Cli_PlcColdStart(Client);
+int TS7Client::PlcStop()
+ return Cli_PlcStop(Client);
+int TS7Client::CopyRamToRom(int Timeout)
+ return Cli_CopyRamToRom(Client, Timeout);
+int TS7Client::Compress(int Timeout)
+ return Cli_Compress(Client, Timeout);
+int TS7Client::GetProtection(PS7Protection pUsrData)
+ return Cli_GetProtection(Client, pUsrData);
+int TS7Client::SetSessionPassword(char *Password)
+ return Cli_SetSessionPassword(Client, Password);
+int TS7Client::ClearSessionPassword()
+ return Cli_ClearSessionPassword(Client);
+int TS7Client::ExecTime()
+ int Time;
+ int Result = Cli_GetExecTime(Client, &Time);
+ if (Result==0)
+ return Time;
+ return Result;
+int TS7Client::LastError()
+ int LastError;
+ int Result =Cli_GetLastError(Client, &LastError);
+ return LastError;
+int TS7Client::PDULength()
+ int Requested, Negotiated;
+ if (Cli_GetPduLength(Client, &Requested, &Negotiated)==0)
+ return Negotiated;
+int TS7Client::PDURequested()
+ return Requested;
+int TS7Client::PlcStatus()
+ int Status;
+ int Result = Cli_GetPlcStatus(Client, &Status);
+ return Status;
+bool TS7Client::Connected()
+ int ClientStatus;
+ if (Cli_GetConnected(Client ,&ClientStatus)==0)
+ return ClientStatus!=0;
+ return false;
+int TS7Client::SetAsCallback(pfn_CliCompletion pCompletion, void *usrPtr)
+ return Cli_SetAsCallback(Client, pCompletion, usrPtr);
+bool TS7Client::CheckAsCompletion(int *opResult)
+ return Cli_CheckAsCompletion(Client ,opResult)==JobComplete;
+int TS7Client::WaitAsCompletion(longword Timeout)
+ return Cli_WaitAsCompletion(Client, Timeout);
+int TS7Client::AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return Cli_AsReadArea(Client, Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int TS7Client::AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return Cli_AsWriteArea(Client, Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int TS7Client::AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int *ItemsCount)
+ return Cli_AsListBlocksOfType(Client, BlockType, pUsrData, ItemsCount);
+int TS7Client::AsReadSZL(int ID, int Index, PS7SZL pUsrData, int *Size)
+ return Cli_AsReadSZL(Client, ID, Index, pUsrData, Size);
+int TS7Client::AsReadSZLList(PS7SZLList pUsrData, int *ItemsCount)
+ return Cli_AsReadSZLList(Client, pUsrData, ItemsCount);
+int TS7Client::AsUpload(int BlockType, int BlockNum, void *pUsrData, int *Size)
+ return Cli_AsUpload(Client, BlockType, BlockNum, pUsrData, Size);
+int TS7Client::AsFullUpload(int BlockType, int BlockNum, void *pUsrData, int *Size)
+ return Cli_AsFullUpload(Client, BlockType, BlockNum, pUsrData, Size);
+int TS7Client::AsDownload(int BlockNum, void *pUsrData, int Size)
+ return Cli_AsDownload(Client, BlockNum, pUsrData, Size);
+int TS7Client::AsCopyRamToRom(int Timeout)
+ return Cli_AsCopyRamToRom(Client, Timeout);
+int TS7Client::AsCompress(int Timeout)
+ return Cli_AsCompress(Client, Timeout);
+int TS7Client::AsDBRead(int DBNumber, int Start, int Size, void *pUsrData)
+ return Cli_AsDBRead(Client, DBNumber, Start, Size, pUsrData);
+int TS7Client::AsDBWrite(int DBNumber, int Start, int Size, void *pUsrData)
+ return Cli_AsDBWrite(Client, DBNumber, Start, Size, pUsrData);
+int TS7Client::AsMBRead(int Start, int Size, void *pUsrData)
+ return Cli_AsMBRead(Client, Start, Size, pUsrData);
+int TS7Client::AsMBWrite(int Start, int Size, void *pUsrData)
+ return Cli_AsMBWrite(Client, Start, Size, pUsrData);
+int TS7Client::AsEBRead(int Start, int Size, void *pUsrData)
+ return Cli_AsEBRead(Client, Start, Size, pUsrData);
+int TS7Client::AsEBWrite(int Start, int Size, void *pUsrData)
+ return Cli_AsEBWrite(Client, Start, Size, pUsrData);
+int TS7Client::AsABRead(int Start, int Size, void *pUsrData)
+ return Cli_AsABRead(Client, Start, Size, pUsrData);
+int TS7Client::AsABWrite(int Start, int Size, void *pUsrData)
+ return Cli_AsABWrite(Client, Start, Size, pUsrData);
+int TS7Client::AsTMRead(int Start, int Amount, void *pUsrData)
+ return Cli_AsTMRead(Client, Start, Amount, pUsrData);
+int TS7Client::AsTMWrite(int Start, int Amount, void *pUsrData)
+ return Cli_AsTMWrite(Client, Start, Amount, pUsrData);
+int TS7Client::AsCTRead(int Start, int Amount, void *pUsrData)
+ return Cli_AsCTRead(Client, Start, Amount, pUsrData);
+int TS7Client::AsCTWrite(int Start, int Amount, void *pUsrData)
+ return Cli_AsCTWrite(Client, Start, Amount, pUsrData);
+int TS7Client::AsDBGet(int DBNumber, void *pUsrData, int *Size)
+ return Cli_AsDBGet(Client, DBNumber, pUsrData, Size);
+int TS7Client::AsDBFill(int DBNumber, int FillChar)
+ return Cli_AsDBFill(Client, DBNumber, FillChar);
+// SERVER
+TS7Server::TS7Server()
+ Server=Srv_Create();
+TS7Server::~TS7Server()
+ Srv_Destroy(&Server);
+int TS7Server::Start()
+ return Srv_Start(Server);
+int TS7Server::StartTo(const char *Address)
+ return Srv_StartTo(Server, Address);
+int TS7Server::Stop()
+ return Srv_Stop(Server);
+int TS7Server::GetParam(int ParamNumber, void *pValue)
+ return Srv_GetParam(Server, ParamNumber, pValue);
+int TS7Server::SetParam(int ParamNumber, void *pValue)
+ return Srv_SetParam(Server, ParamNumber, pValue);
+int TS7Server::SetEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr)
+ return Srv_SetEventsCallback(Server, PCallBack, UsrPtr);
+int TS7Server::SetReadEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr)
+ return Srv_SetReadEventsCallback(Server, PCallBack, UsrPtr);
+int TS7Server::SetRWAreaCallback(pfn_RWAreaCallBack PCallBack, void *UsrPtr)
+ return Srv_SetRWAreaCallback(Server, PCallBack, UsrPtr);
+bool TS7Server::PickEvent(TSrvEvent *pEvent)
+ int EvtReady;
+ if (Srv_PickEvent(Server, pEvent, &EvtReady)==0)
+ return EvtReady!=0;
+void TS7Server::ClearEvents()
+ Srv_ClearEvents(Server);
+longword TS7Server::GetEventsMask()
+ longword Mask;
+ int Result = Srv_GetMask(Server, mkEvent, &Mask);
+ return Mask;
+longword TS7Server::GetLogMask()
+ int Result = Srv_GetMask(Server, mkLog, &Mask);
+void TS7Server::SetEventsMask(longword Mask)
+ Srv_SetMask(Server, mkEvent, Mask);
+void TS7Server::SetLogMask(longword Mask)
+ Srv_SetMask(Server, mkLog, Mask);
+int TS7Server::RegisterArea(int AreaCode, word Index, void *pUsrData, word Size)
+ return Srv_RegisterArea(Server, AreaCode, Index, pUsrData, Size);
+int TS7Server::UnregisterArea(int AreaCode, word Index)
+ return Srv_UnregisterArea(Server, AreaCode, Index);
+int TS7Server::LockArea(int AreaCode, word Index)
+ return Srv_LockArea(Server, AreaCode, Index);
+int TS7Server::UnlockArea(int AreaCode, word Index)
+ return Srv_UnlockArea(Server, AreaCode, Index);
+int TS7Server::ServerStatus()
+ int ServerStatus, CpuStatus, ClientsCount;
+ int Result =Srv_GetStatus(Server, &ServerStatus, &CpuStatus, &ClientsCount);
+ return ServerStatus;
+int TS7Server::GetCpuStatus()
+ return CpuStatus;
+int TS7Server::ClientsCount()
+ return ClientsCount;
+int TS7Server::SetCpuStatus(int Status)
+ return Srv_SetCpuStatus(Server, Status);
+// PARTNER
+TS7Partner::TS7Partner(bool Active)
+ Partner=Par_Create(int(Active));
+TS7Partner::~TS7Partner()
+ Par_Destroy(&Partner);
+int TS7Partner::GetParam(int ParamNumber, void *pValue)
+ return Par_GetParam(Partner, ParamNumber, pValue);
+int TS7Partner::SetParam(int ParamNumber, void *pValue)
+ return Par_SetParam(Partner, ParamNumber, pValue);
+int TS7Partner::Start()
+ return Par_Start(Partner);
+int TS7Partner::StartTo(const char *LocalAddress, const char *RemoteAddress, int LocalTSAP, int RemoteTSAP)
+ return Par_StartTo(Partner, LocalAddress, RemoteAddress, LocalTSAP, RemoteTSAP);
+int TS7Partner::Stop()
+ return Par_Stop(Partner);
+int TS7Partner::BSend(longword R_ID, void *pUsrData, int Size)
+ return Par_BSend(Partner, R_ID, pUsrData, Size);
+int TS7Partner::AsBSend(longword R_ID, void *pUsrData, int Size)
+ return Par_AsBSend(Partner, R_ID, pUsrData, Size);
+bool TS7Partner::CheckAsBSendCompletion(int *opResult)
+ return Par_CheckAsBSendCompletion(Partner ,opResult)==JobComplete;
+int TS7Partner::WaitAsBSendCompletion(longword Timeout)
+ return Par_WaitAsBSendCompletion(Partner, Timeout);
+int TS7Partner::SetSendCallback(pfn_ParSendCompletion pCompletion, void *usrPtr)
+ return Par_SetSendCallback(Partner, pCompletion, usrPtr);
+int TS7Partner::BRecv(longword *R_ID, void *pUsrData, int *Size, longword Timeout)
+ return Par_BRecv(Partner, R_ID, pUsrData, Size, Timeout);
+bool TS7Partner::CheckAsBRecvCompletion(int *opResult, longword *R_ID, void *pUsrData, int *Size)
+ return Par_CheckAsBRecvCompletion(Partner, opResult, R_ID, pUsrData, Size) == JobComplete;
+int TS7Partner::SetRecvCallback(pfn_ParRecvCallBack pCallback, void *usrPtr)
+ return Par_SetRecvCallback(Partner, pCallback, usrPtr);
+int TS7Partner::Status()
+ int ParStatus;
+ int Result = Par_GetStatus(Partner, &ParStatus);
+ return ParStatus;
+int TS7Partner::LastError()
+ int Result = Par_GetLastError(Partner, &Error);
+ return Error;
+int TS7Partner::GetTimes(longword *SendTime, longword *RecvTime)
+ return Par_GetTimes(Partner, SendTime, RecvTime);
+int TS7Partner::GetStats(longword *BytesSent, longword *BytesRecv, longword *ErrSend, longword *ErrRecv)
+ return Par_GetStats(Partner, BytesSent, BytesRecv, ErrSend, ErrRecv);
+bool TS7Partner::Linked()
+ return Status()==par_linked;
+// Text routines
+TextString CliErrorText(int Error)
+ char text[TextLen];
+ Cli_ErrorText(Error, text, TextLen);
+ return TextString(text);
+TextString SrvErrorText(int Error)
+ Srv_ErrorText(Error, text, TextLen);
+TextString ParErrorText(int Error)
+ Par_ErrorText(Error, text, TextLen);
+TextString SrvEventText(TSrvEvent *Event)
+ Srv_EventText(Event, text, TextLen);
@@ -0,0 +1,957 @@
+| C/C++ Snap 7 classes/Imports definitions |
+#ifndef snap7_h
+#define snap7_h
+// Platform detection
+#if defined (_WIN32)||defined(_WIN64)||defined(__WIN32__)||defined(__WINDOWS__)
+# define OS_WINDOWS
+// Visual Studio needs this to use the correct time_t size
+#if defined (_WIN32) && !defined(_WIN64)
+# define _USE_32BIT_TIME_T
+#if defined(unix) || defined(__unix__) || defined(__unix)
+# define PLATFORM_UNIX
+#if defined(__SVR4) || defined(__svr4__)
+# define OS_SOLARIS
+#if BSD>=0
+# define OS_BSD
+#if defined(__APPLE__)
+# define OS_OSX
+#if defined(PLATFORM_UNIX) || defined(OS_OSX)
+# include <unistd.h>
+# if defined(_POSIX_VERSION)
+# define POSIX
+# endif
+// C++ Library
+#ifdef __cplusplus
+#include <string>
+#include <time.h>
+// Visual C++ not C99 compliant (VS2008--)
+#ifdef _MSC_VER
+# if _MSC_VER >= 1600
+# include <stdint.h> // VS2010++ have it
+# else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef signed __int64 int64_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+ typedef unsigned __int64 uint64_t;
+ #ifdef _WIN64
+ typedef unsigned __int64 uintptr_t;
+ #else
+ typedef unsigned __int32 uintptr_t;
+ #endif
+# include <stdint.h>
+extern "C" {
+// C exact length types
+#ifndef __cplusplus
+#ifdef OS_BSD
+# include <time.h>
+#ifdef OS_OSX
+#ifdef OS_SOLARIS
+#if defined(_UINTPTR_T_DEFINED)
+#if !defined(_UINTPTR_T_DEFINED) && !defined(OS_SOLARIS) && !defined(OS_BSD) && !defined(OS_OSX)
+ typedef unsigned char uint8_t; // 8 bit unsigned integer
+ typedef unsigned short uint16_t; // 16 bit unsigned integer
+ typedef unsigned int uint32_t; // 32 bit unsigned integer
+ typedef unsigned long uintptr_t;// 64 bit unsigned integer
+# define S7API __stdcall
+# define S7API
+#pragma pack(1)
+//******************************************************************************
+// COMMON
+// Exact length types regardless of platform/processor
+typedef uint8_t byte;
+typedef uint16_t word;
+typedef uint32_t longword;
+typedef byte *pbyte;
+typedef word *pword;
+typedef uintptr_t S7Object; // multi platform/processor object reference
+ // DON'T CONFUSE IT WITH AN OLE OBJECT, IT'S SIMPLY
+ // AN INTEGER VALUE (32 OR 64 BIT) USED AS HANDLE.
+typedef struct
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+}tm;
+typedef int bool;
+#define false 0;
+#define true 1;
+const int errLibInvalidParam = -1;
+const int errLibInvalidObject = -2;
+// CPU status
+#define S7CpuStatusUnknown 0x00
+#define S7CpuStatusRun 0x08
+#define S7CpuStatusStop 0x04
+// ISO Errors
+const longword errIsoConnect = 0x00010000; // Connection error
+const longword errIsoDisconnect = 0x00020000; // Disconnect error
+const longword errIsoInvalidPDU = 0x00030000; // Bad format
+const longword errIsoInvalidDataSize = 0x00040000; // Bad Datasize passed to send/recv buffer is invalid
+const longword errIsoNullPointer = 0x00050000; // Null passed as pointer
+const longword errIsoShortPacket = 0x00060000; // A short packet received
+const longword errIsoTooManyFragments = 0x00070000; // Too many packets without EoT flag
+const longword errIsoPduOverflow = 0x00080000; // The sum of fragments data exceded maximum packet size
+const longword errIsoSendPacket = 0x00090000; // An error occurred during send
+const longword errIsoRecvPacket = 0x000A0000; // An error occurred during recv
+const longword errIsoInvalidParams = 0x000B0000; // Invalid TSAP params
+const longword errIsoResvd_1 = 0x000C0000; // Unassigned
+const longword errIsoResvd_2 = 0x000D0000; // Unassigned
+const longword errIsoResvd_3 = 0x000E0000; // Unassigned
+const longword errIsoResvd_4 = 0x000F0000; // Unassigned
+// Tag Struct
+typedef struct{
+ int Area;
+ int DBNumber;
+ int Start;
+ int Size;
+ int WordLen;
+}TS7Tag, *PS7Tag;
+// PARAMS LIST
+const int p_u16_LocalPort = 1;
+const int p_u16_RemotePort = 2;
+const int p_i32_PingTimeout = 3;
+const int p_i32_SendTimeout = 4;
+const int p_i32_RecvTimeout = 5;
+const int p_i32_WorkInterval = 6;
+const int p_u16_SrcRef = 7;
+const int p_u16_DstRef = 8;
+const int p_u16_SrcTSap = 9;
+const int p_i32_PDURequest = 10;
+const int p_i32_MaxClients = 11;
+const int p_i32_BSendTimeout = 12;
+const int p_i32_BRecvTimeout = 13;
+const int p_u32_RecoveryTime = 14;
+const int p_u32_KeepAliveTime = 15;
+// Client/Partner Job status
+const int JobComplete = 0;
+const int JobPending = 1;
+// Error codes
+const longword errNegotiatingPDU = 0x00100000;
+const longword errCliInvalidParams = 0x00200000;
+const longword errCliJobPending = 0x00300000;
+const longword errCliTooManyItems = 0x00400000;
+const longword errCliInvalidWordLen = 0x00500000;
+const longword errCliPartialDataWritten = 0x00600000;
+const longword errCliSizeOverPDU = 0x00700000;
+const longword errCliInvalidPlcAnswer = 0x00800000;
+const longword errCliAddressOutOfRange = 0x00900000;
+const longword errCliInvalidTransportSize = 0x00A00000;
+const longword errCliWriteDataSizeMismatch = 0x00B00000;
+const longword errCliItemNotAvailable = 0x00C00000;
+const longword errCliInvalidValue = 0x00D00000;
+const longword errCliCannotStartPLC = 0x00E00000;
+const longword errCliAlreadyRun = 0x00F00000;
+const longword errCliCannotStopPLC = 0x01000000;
+const longword errCliCannotCopyRamToRom = 0x01100000;
+const longword errCliCannotCompress = 0x01200000;
+const longword errCliAlreadyStop = 0x01300000;
+const longword errCliFunNotAvailable = 0x01400000;
+const longword errCliUploadSequenceFailed = 0x01500000;
+const longword errCliInvalidDataSizeRecvd = 0x01600000;
+const longword errCliInvalidBlockType = 0x01700000;
+const longword errCliInvalidBlockNumber = 0x01800000;
+const longword errCliInvalidBlockSize = 0x01900000;
+const longword errCliDownloadSequenceFailed = 0x01A00000;
+const longword errCliInsertRefused = 0x01B00000;
+const longword errCliDeleteRefused = 0x01C00000;
+const longword errCliNeedPassword = 0x01D00000;
+const longword errCliInvalidPassword = 0x01E00000;
+const longword errCliNoPasswordToSetOrClear = 0x01F00000;
+const longword errCliJobTimeout = 0x02000000;
+const longword errCliPartialDataRead = 0x02100000;
+const longword errCliBufferTooSmall = 0x02200000;
+const longword errCliFunctionRefused = 0x02300000;
+const longword errCliDestroying = 0x02400000;
+const longword errCliInvalidParamNumber = 0x02500000;
+const longword errCliCannotChangeParam = 0x02600000;
+const int MaxVars = 20; // Max vars that can be transferred with MultiRead/MultiWrite
+// Client Connection Type
+const word CONNTYPE_PG = 0x0001; // Connect to the PLC as a PG
+const word CONNTYPE_OP = 0x0002; // Connect to the PLC as an OP
+const word CONNTYPE_BASIC = 0x0003; // Basic connection
+// Area ID
+const byte S7AreaPE = 0x81;
+const byte S7AreaPA = 0x82;
+const byte S7AreaMK = 0x83;
+const byte S7AreaDB = 0x84;
+const byte S7AreaCT = 0x1C;
+const byte S7AreaTM = 0x1D;
+// Word Length
+const int S7WLBit = 0x01;
+const int S7WLByte = 0x02;
+const int S7WLWord = 0x04;
+const int S7WLDWord = 0x06;
+const int S7WLReal = 0x08;
+const int S7WLCounter = 0x1C;
+const int S7WLTimer = 0x1D;
+// Block type
+const byte Block_OB = 0x38;
+const byte Block_DB = 0x41;
+const byte Block_SDB = 0x42;
+const byte Block_FC = 0x43;
+const byte Block_SFC = 0x44;
+const byte Block_FB = 0x45;
+const byte Block_SFB = 0x46;
+// Sub Block Type
+const byte SubBlk_OB = 0x08;
+const byte SubBlk_DB = 0x0A;
+const byte SubBlk_SDB = 0x0B;
+const byte SubBlk_FC = 0x0C;
+const byte SubBlk_SFC = 0x0D;
+const byte SubBlk_FB = 0x0E;
+const byte SubBlk_SFB = 0x0F;
+// Block languages
+const byte BlockLangAWL = 0x01;
+const byte BlockLangKOP = 0x02;
+const byte BlockLangFUP = 0x03;
+const byte BlockLangSCL = 0x04;
+const byte BlockLangDB = 0x05;
+const byte BlockLangGRAPH = 0x06;
+// Read/Write Multivars
+ int Result;
+ int Amount;
+ void *pdata;
+} TS7DataItem, *PS7DataItem;
+//typedef int TS7ResultItems[MaxVars];
+//typedef TS7ResultItems *PS7ResultItems;
+// List Blocks
+typedef struct {
+ int OBCount;
+ int FBCount;
+ int FCCount;
+ int SFBCount;
+ int SFCCount;
+ int DBCount;
+ int SDBCount;
+} TS7BlocksList, *PS7BlocksList;
+// Blocks info
+ int BlkType; // Block Type (OB, DB)
+ int BlkNumber; // Block number
+ int BlkLang; // Block Language
+ int BlkFlags; // Block flags
+ int MC7Size; // The real size in bytes
+ int LoadSize; // Load memory size
+ int LocalData; // Local data
+ int SBBLength; // SBB Length
+ int CheckSum; // Checksum
+ int Version; // Block version
+ // Chars info
+ char CodeDate[11]; // Code date
+ char IntfDate[11]; // Interface date
+ char Author[9]; // Author
+ char Family[9]; // Family
+ char Header[9]; // Header
+} TS7BlockInfo, *PS7BlockInfo ;
+typedef word TS7BlocksOfType[0x2000];
+typedef TS7BlocksOfType *PS7BlocksOfType;
+// Order code
+ char Code[21];
+ byte V1;
+ byte V2;
+ byte V3;
+} TS7OrderCode, *PS7OrderCode;
+// CPU Info
+ char ModuleTypeName[33];
+ char SerialNumber[25];
+ char ASName[25];
+ char Copyright[27];
+ char ModuleName[25];
+} TS7CpuInfo, *PS7CpuInfo;
+ int MaxPduLengt;
+ int MaxConnections;
+ int MaxMpiRate;
+ int MaxBusRate;
+} TS7CpInfo, *PS7CpInfo;
+// See §33.1 of "System Software for S7-300/400 System and Standard Functions"
+// and see SFC51 description too
+ word LENTHDR;
+ word N_DR;
+} SZL_HEADER, *PSZL_HEADER;
+ SZL_HEADER Header;
+ byte Data[0x4000-4];
+} TS7SZL, *PS7SZL;
+// SZL List of available SZL IDs : same as SZL but List items are big-endian adjusted
+ word List[0x2000-2];
+} TS7SZLList, *PS7SZLList;
+// See §33.19 of "System Software for S7-300/400 System and Standard Functions"
+ word sch_schal;
+ word sch_par;
+ word sch_rel;
+ word bart_sch;
+ word anl_sch;
+} TS7Protection, *PS7Protection;
+// Client completion callback
+typedef void (S7API *pfn_CliCompletion) (void *usrPtr, int opCode, int opResult);
+// Import prototypes
+S7Object S7API Cli_Create();
+void S7API Cli_Destroy(S7Object *Client);
+int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot);
+int S7API Cli_SetConnectionParams(S7Object Client, const char *Address, word LocalTSAP, word RemoteTSAP);
+int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType);
+int S7API Cli_Connect(S7Object Client);
+int S7API Cli_Disconnect(S7Object Client);
+int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue);
+int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue);
+int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr);
+// Data I/O main functions
+int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
+int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
+// Data I/O Lean functions
+int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+// Directory functions
+int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData);
+int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData);
+int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size);
+int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount);
+// Blocks functions
+int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
+int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
+int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size);
+int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum);
+int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int *Size);
+int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar);
+// Date/Time functions
+int S7API Cli_GetPlcDateTime(S7Object Client, tm *DateTime);
+int S7API Cli_SetPlcDateTime(S7Object Client, tm *DateTime);
+int S7API Cli_SetPlcSystemDateTime(S7Object Client);
+// System Info functions
+int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData);
+int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData);
+int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData);
+int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int *Size);
+int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int *ItemsCount);
+// Control functions
+int S7API Cli_PlcHotStart(S7Object Client);
+int S7API Cli_PlcColdStart(S7Object Client);
+int S7API Cli_PlcStop(S7Object Client);
+int S7API Cli_CopyRamToRom(S7Object Client, int Timeout);
+int S7API Cli_Compress(S7Object Client, int Timeout);
+int S7API Cli_GetPlcStatus(S7Object Client, int *Status);
+// Security functions
+int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData);
+int S7API Cli_SetSessionPassword(S7Object Client, char *Password);
+int S7API Cli_ClearSessionPassword(S7Object Client);
+// Low level
+int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int *Size);
+// Misc
+int S7API Cli_GetExecTime(S7Object Client, int *Time);
+int S7API Cli_GetLastError(S7Object Client, int *LastError);
+int S7API Cli_GetPduLength(S7Object Client, int *Requested, int *Negotiated);
+int S7API Cli_ErrorText(int Error, char *Text, int TextLen);
+// 1.1.0
+int S7API Cli_GetConnected(S7Object Client, int *Connected);
+// Async functions
+int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData);
+int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount);
+int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int *Size);
+int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int *ItemsCount);
+int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
+int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int *Size);
+int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size);
+int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout);
+int S7API Cli_AsCompress(S7Object Client, int Timeout);
+int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int *Size);
+int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar);
+int S7API Cli_CheckAsCompletion(S7Object Client, int *opResult);
+int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout);
+const int OperationRead = 0;
+const int OperationWrite = 1;
+const int mkEvent = 0;
+const int mkLog = 1;
+// Server Area ID (use with Register/unregister - Lock/unlock Area)
+const int srvAreaPE = 0;
+const int srvAreaPA = 1;
+const int srvAreaMK = 2;
+const int srvAreaCT = 3;
+const int srvAreaTM = 4;
+const int srvAreaDB = 5;
+// Errors
+const longword errSrvCannotStart = 0x00100000; // Server cannot start
+const longword errSrvDBNullPointer = 0x00200000; // Passed null as PData
+const longword errSrvAreaAlreadyExists = 0x00300000; // Area Re-registration
+const longword errSrvUnknownArea = 0x00400000; // Unknown area
+const longword errSrvInvalidParams = 0x00500000; // Invalid param(s) supplied
+const longword errSrvTooManyDB = 0x00600000; // Cannot register DB
+const longword errSrvInvalidParamNumber = 0x00700000; // Invalid param (srv_get/set_param)
+const longword errSrvCannotChangeParam = 0x00800000; // Cannot change because running
+// TCP Server Event codes
+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; // actually unused
+const longword evcReserved_00000800 = 0x00000800; // actually unused
+const longword evcReserved_00001000 = 0x00001000; // actually unused
+const longword evcReserved_00002000 = 0x00002000; // actually unused
+const longword evcReserved_00004000 = 0x00004000; // actually unused
+const longword evcReserved_00008000 = 0x00008000; // actually unused
+// S7 Server Event Code
+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; // actually unused
+const longword evcReserved_10000000 = 0x10000000; // actually unused
+const longword evcReserved_20000000 = 0x20000000; // actually unused
+const longword evcReserved_40000000 = 0x40000000; // actually unused
+const longword evcReserved_80000000 = 0x80000000; // actually unused
+// Masks to enable/disable all events
+const longword evcAll = 0xFFFFFFFF;
+const longword evcNone = 0x00000000;
+// Event SubCodes
+const word evsUnknown = 0x0000;
+const word evsStartUpload = 0x0001;
+const word evsStartDownload = 0x0001;
+const word evsGetBlockList = 0x0001;
+const word evsStartListBoT = 0x0002;
+const word evsListBoT = 0x0003;
+const word evsGetBlockInfo = 0x0004;
+const word evsGetClock = 0x0001;
+const word evsSetClock = 0x0002;
+const word evsSetPassword = 0x0001;
+const word evsClrPassword = 0x0002;
+// Event Params : functions group
+const word grProgrammer = 0x0041;
+const word grCyclicData = 0x0042;
+const word grBlocksInfo = 0x0043;
+const word grSZL = 0x0044;
+const word grPassword = 0x0045;
+const word grBSend = 0x0046;
+const word grClock = 0x0047;
+const word grSecurity = 0x0045;
+// Event Params : control codes
+const word CodeControlUnknown = 0x0000;
+const word CodeControlColdStart = 0x0001;
+const word CodeControlWarmStart = 0x0002;
+const word CodeControlStop = 0x0003;
+const word CodeControlCompress = 0x0004;
+const word CodeControlCpyRamRom = 0x0005;
+const word CodeControlInsDel = 0x0006;
+// Event Result
+const word evrNoError = 0x0000;
+const word evrFragmentRejected = 0x0001;
+const word evrMalformedPDU = 0x0002;
+const word evrSparseBytes = 0x0003;
+const word evrCannotHandlePDU = 0x0004;
+const word evrNotImplemented = 0x0005;
+const word evrErrException = 0x0006;
+const word evrErrAreaNotFound = 0x0007;
+const word evrErrOutOfRange = 0x0008;
+const word evrErrOverPDU = 0x0009;
+const word evrErrTransportSize = 0x000A;
+const word evrInvalidGroupUData = 0x000B;
+const word evrInvalidSZL = 0x000C;
+const word evrDataSizeMismatch = 0x000D;
+const word evrCannotUpload = 0x000E;
+const word evrCannotDownload = 0x000F;
+const word evrUploadInvalidID = 0x0010;
+const word evrResNotFound = 0x0011;
+ 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, *PSrvEvent;
+// Server Events callback
+typedef void (S7API *pfn_SrvCallBack)(void *usrPtr, PSrvEvent PEvent, int Size);
+// Server Read/Write callback
+typedef int(S7API *pfn_RWAreaCallBack)(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData);
+S7Object S7API Srv_Create();
+void S7API Srv_Destroy(S7Object *Server);
+int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue);
+int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue);
+int S7API Srv_StartTo(S7Object Server, const char *Address);
+int S7API Srv_Start(S7Object Server);
+int S7API Srv_Stop(S7Object Server);
+int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size);
+int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index);
+int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index);
+int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index);
+int S7API Srv_GetStatus(S7Object Server, int *ServerStatus, int *CpuStatus, int *ClientsCount);
+int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus);
+int S7API Srv_ClearEvents(S7Object Server);
+int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int *EvtReady);
+int S7API Srv_GetMask(S7Object Server, int MaskKind, longword *Mask);
+int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask);
+int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
+int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
+int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr);
+int S7API Srv_EventText(TSrvEvent *Event, char *Text, int TextLen);
+int S7API Srv_ErrorText(int Error, char *Text, int TextLen);
+// Status
+const int par_stopped = 0; // stopped
+const int par_connecting = 1; // running and active connecting
+const int par_waiting = 2; // running and waiting for a connection
+const int par_linked = 3; // running and connected : linked
+const int par_sending = 4; // sending data
+const int par_receiving = 5; // receiving data
+const int par_binderror = 6; // error starting passive server
+const longword errParAddressInUse = 0x00200000;
+const longword errParNoRoom = 0x00300000;
+const longword errServerNoRoom = 0x00400000;
+const longword errParInvalidParams = 0x00500000;
+const longword errParNotLinked = 0x00600000;
+const longword errParBusy = 0x00700000;
+const longword errParFrameTimeout = 0x00800000;
+const longword errParInvalidPDU = 0x00900000;
+const longword errParSendTimeout = 0x00A00000;
+const longword errParRecvTimeout = 0x00B00000;
+const longword errParSendRefused = 0x00C00000;
+const longword errParNegotiatingPDU = 0x00D00000;
+const longword errParSendingBlock = 0x00E00000;
+const longword errParRecvingBlock = 0x00F00000;
+const longword errParBindError = 0x01000000;
+const longword errParDestroying = 0x01100000;
+const longword errParInvalidParamNumber = 0x01200000; // Invalid param (par_get/set_param)
+const longword errParCannotChangeParam = 0x01300000; // Cannot change because running
+const longword errParBufferTooSmall = 0x01400000; // Raised by LabVIEW wrapper
+// Brecv Data incoming Callback
+typedef void (S7API *pfn_ParRecvCallBack)(void * usrPtr, int opResult, longword R_ID, void *pData, int Size);
+// BSend Completion Callback
+typedef void (S7API *pfn_ParSendCompletion)(void * usrPtr, int opResult);
+S7Object S7API Par_Create(int Active);
+void S7API Par_Destroy(S7Object *Partner);
+int S7API Par_GetParam(S7Object Partner, int ParamNumber, void *pValue);
+int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue);
+int S7API Par_StartTo(S7Object Partner, const char *LocalAddress, const char *RemoteAddress,
+ word LocTsap, word RemTsap);
+int S7API Par_Start(S7Object Partner);
+int S7API Par_Stop(S7Object Partner);
+// BSend
+int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
+int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
+int S7API Par_CheckAsBSendCompletion(S7Object Partner, int *opResult);
+int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout);
+int S7API Par_SetSendCallback(S7Object Partner, pfn_ParSendCompletion pCompletion, void *usrPtr);
+// BRecv
+int S7API Par_BRecv(S7Object Partner, longword *R_ID, void *pData, int *Size, longword Timeout);
+int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int *opResult, longword *R_ID,
+ void *pData, int *Size);
+int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParRecvCallBack pCompletion, void *usrPtr);
+// Stat
+int S7API Par_GetTimes(S7Object Partner, longword *SendTime, longword *RecvTime);
+int S7API Par_GetStats(S7Object Partner, longword *BytesSent, longword *BytesRecv,
+ longword *SendErrors, longword *RecvErrors);
+int S7API Par_GetLastError(S7Object Partner, int *LastError);
+int S7API Par_GetStatus(S7Object Partner, int *Status);
+int S7API Par_ErrorText(int Error, char *Text, int TextLen);
+#pragma pack()
+#endif // __cplusplus
+// CLIENT CLASS DEFINITION
+class TS7Client
+private:
+ S7Object Client;
+public:
+ TS7Client();
+ ~TS7Client();
+ // Control functions
+ int Connect();
+ int ConnectTo(const char *RemAddress, int Rack, int Slot);
+ int SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTSAP);
+ int SetConnectionType(word ConnectionType);
+ int Disconnect();
+ int GetParam(int ParamNumber, void *pValue);
+ int SetParam(int ParamNumber, void *pValue);
+ // Data I/O Main functions
+ int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+ int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+ int ReadMultiVars(PS7DataItem Item, int ItemsCount);
+ int WriteMultiVars(PS7DataItem Item, int ItemsCount);
+ // Data I/O Lean functions
+ int DBRead(int DBNumber, int Start, int Size, void *pUsrData);
+ int DBWrite(int DBNumber, int Start, int Size, void *pUsrData);
+ int MBRead(int Start, int Size, void *pUsrData);
+ int MBWrite(int Start, int Size, void *pUsrData);
+ int EBRead(int Start, int Size, void *pUsrData);
+ int EBWrite(int Start, int Size, void *pUsrData);
+ int ABRead(int Start, int Size, void *pUsrData);
+ int ABWrite(int Start, int Size, void *pUsrData);
+ int TMRead(int Start, int Amount, void *pUsrData);
+ int TMWrite(int Start, int Amount, void *pUsrData);
+ int CTRead(int Start, int Amount, void *pUsrData);
+ int CTWrite(int Start, int Amount, void *pUsrData);
+ // Directory functions
+ int ListBlocks(PS7BlocksList pUsrData);
+ int GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData);
+ int GetPgBlockInfo(void *pBlock, PS7BlockInfo pUsrData, int Size);
+ int ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int *ItemsCount);
+ // Blocks functions
+ int Upload(int BlockType, int BlockNum, void *pUsrData, int *Size);
+ int FullUpload(int BlockType, int BlockNum, void *pUsrData, int *Size);
+ int Download(int BlockNum, void *pUsrData, int Size);
+ int Delete(int BlockType, int BlockNum);
+ int DBGet(int DBNumber, void *pUsrData, int *Size);
+ int DBFill(int DBNumber, int FillChar);
+ // Date/Time functions
+ int GetPlcDateTime(tm *DateTime);
+ int SetPlcDateTime(tm *DateTime);
+ int SetPlcSystemDateTime();
+ // System Info functions
+ int GetOrderCode(PS7OrderCode pUsrData);
+ int GetCpuInfo(PS7CpuInfo pUsrData);
+ int GetCpInfo(PS7CpInfo pUsrData);
+ int ReadSZL(int ID, int Index, PS7SZL pUsrData, int *Size);
+ int ReadSZLList(PS7SZLList pUsrData, int *ItemsCount);
+ int PlcHotStart();
+ int PlcColdStart();
+ int PlcStop();
+ int CopyRamToRom(int Timeout);
+ int Compress(int Timeout);
+ // Security functions
+ int GetProtection(PS7Protection pUsrData);
+ int SetSessionPassword(char *Password);
+ int ClearSessionPassword();
+ // Properties
+ int ExecTime();
+ int LastError();
+ int PDURequested();
+ int PDULength();
+ int PlcStatus();
+ bool Connected();
+ // Async functions
+ int SetAsCallback(pfn_CliCompletion pCompletion, void *usrPtr);
+ bool CheckAsCompletion(int *opResult);
+ int WaitAsCompletion(longword Timeout);
+ int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+ int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+ int AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int *ItemsCount);
+ int AsReadSZL(int ID, int Index, PS7SZL pUsrData, int *Size);
+ int AsReadSZLList(PS7SZLList pUsrData, int *ItemsCount);
+ int AsUpload(int BlockType, int BlockNum, void *pUsrData, int *Size);
+ int AsFullUpload(int BlockType, int BlockNum, void *pUsrData, int *Size);
+ int AsDownload(int BlockNum, void *pUsrData, int Size);
+ int AsCopyRamToRom(int Timeout);
+ int AsCompress(int Timeout);
+ int AsDBRead(int DBNumber, int Start, int Size, void *pUsrData);
+ int AsDBWrite(int DBNumber, int Start, int Size, void *pUsrData);
+ int AsMBRead(int Start, int Size, void *pUsrData);
+ int AsMBWrite(int Start, int Size, void *pUsrData);
+ int AsEBRead(int Start, int Size, void *pUsrData);
+ int AsEBWrite(int Start, int Size, void *pUsrData);
+ int AsABRead(int Start, int Size, void *pUsrData);
+ int AsABWrite(int Start, int Size, void *pUsrData);
+ int AsTMRead(int Start, int Amount, void *pUsrData);
+ int AsTMWrite(int Start, int Amount, void *pUsrData);
+ int AsCTRead(int Start, int Amount, void *pUsrData);
+ int AsCTWrite(int Start, int Amount, void *pUsrData);
+ int AsDBGet(int DBNumber, void *pUsrData, int *Size);
+ int AsDBFill(int DBNumber, int FillChar);
+typedef TS7Client *PS7Client;
+// SERVER CLASS DEFINITION
+class TS7Server
+ S7Object Server;
+ TS7Server();
+ ~TS7Server();
+ // Control
+ int Start();
+ int StartTo(const char *Address);
+ int Stop();
+ // Events
+ int SetEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr);
+ int SetReadEventsCallback(pfn_SrvCallBack PCallBack, void *UsrPtr);
+ int SetRWAreaCallback(pfn_RWAreaCallBack PCallBack, void *UsrPtr);
+ bool PickEvent(TSrvEvent *pEvent);
+ void ClearEvents();
+ longword GetEventsMask();
+ longword GetLogMask();
+ void SetEventsMask(longword Mask);
+ void SetLogMask(longword Mask);
+ // Resources
+ int RegisterArea(int AreaCode, word Index, void *pUsrData, word Size);
+ int UnregisterArea(int AreaCode, word Index);
+ int LockArea(int AreaCode, word Index);
+ int UnlockArea(int AreaCode, word Index);
+ int ServerStatus();
+ int GetCpuStatus();
+ int SetCpuStatus(int Status);
+ int ClientsCount();
+typedef TS7Server *PS7Server;
+// PARTNER CLASS DEFINITION
+class TS7Partner
+ S7Object Partner; // Partner Handle
+ TS7Partner(bool Active);
+ ~TS7Partner();
+ int StartTo(const char *LocalAddress,
+ const char *RemoteAddress,
+ int LocalTSAP,
+ int RemoteTSAP);
+ // Data I/O functions : BSend
+ int BSend(longword R_ID, void *pUsrData, int Size);
+ int AsBSend(longword R_ID, void *pUsrData, int Size);
+ bool CheckAsBSendCompletion(int *opResult);
+ int WaitAsBSendCompletion(longword Timeout);
+ int SetSendCallback(pfn_ParSendCompletion pCompletion, void *usrPtr);
+ // Data I/O functions : BRecv
+ int BRecv(longword *R_ID, void *pUsrData, int *Size, longword Timeout);
+ bool CheckAsBRecvCompletion(int *opResult, longword *R_ID, void *pUsrData, int *Size);
+ int SetRecvCallback(pfn_ParRecvCallBack pCallback, void *usrPtr);
+ int Status();
+ int GetTimes(longword *SendTime, longword *RecvTime);
+ int GetStats(longword *BytesSent,
+ longword *BytesRecv,
+ longword *ErrSend,
+ longword *ErrRecv);
+ bool Linked();
+typedef TS7Partner *PS7Partner;
+// TEXT ROUTINES
+// Only for C++, for pure C use xxx_ErrorText() which uses *char
+#define TextLen 1024
+// String type
+// Here we define generic TextString (by default mapped onto std::string).
+// So you can change it if needed (Unicodestring, Ansistring etc...)
+typedef std::string TextString;
+TextString CliErrorText(int Error);
+TextString SrvErrorText(int Error);
+TextString ParErrorText(int Error);
+TextString SrvEventText(TSrvEvent *Event);
+#endif // snap7_h
@@ -0,0 +1,200 @@
+| Resourceless Server example (1.4.0) |
+| The Server ha not shared resources. On every Read/Write request a callback |
+| is invoked. |
+| To the callback are passed the Tag (Area, Start...) and a pointer to a data |
+| that you can read/write. |
+ typedef byte TRWBuffer[1024];
+ typedef byte *PRWBuffer;
+ void hexdump(void *mem, unsigned int len)
+ for (i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
+ if (i % HEXDUMP_COLS == 0)
+ if (i < len)
+ if (i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
+ for (j = i - (HEXDUMP_COLS - 1); j <= i; j++)
+ if (j >= len) /* end of block, not really printing */
+ else if (isprint((((char*)mem)[j] & 0x7F))) /* printable char */
+// Read/Write callback
+int S7API RWAreaCallBack(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData)
+ PRWBuffer PBuffer = PRWBuffer(pUsrData);
+ if (Operation == OperationRead)
+ printf("Read Request\n");
+ printf("Write Request\n");
+ switch (PTag->Area)
+ case S7AreaPE: printf("Area : PE, ");
+ break;
+ case S7AreaPA: printf("Area : PA, ");
+ case S7AreaMK: printf("Area : MK, ");
+ case S7AreaCT: printf("Area : CT, ");
+ case S7AreaTM: printf("Area : TM, ");
+ case S7AreaDB: printf("Area : DB%d, ", PTag->DBNumber);
+ default: printf("Unknown area %d, ", PTag->Area);
+ printf("Start : %d, ", PTag->Start);
+ printf("Size : %d\n", PTag->Size);
+ if (Operation == OperationWrite)
+ hexdump(pUsrData, PTag->Size);
+ for (int c = 0; c < 1024; c++)
+ PBuffer[c] = cnt;
+ printf("%s\n", SrvEventText(PEvent).c_str());
+ // Filter a bit of noise
+ Server->SetEventsMask(0x3ff);
+ // Set the Read/Write callback
+ Server->SetRWAreaCallback(RWAreaCallBack, NULL);
+ Error = Server->Start();
+ printf("%s\n", SrvErrorText(Error).c_str());
@@ -0,0 +1,141 @@
+| Active Partner Example |
+#include <ctype.h>
+#define size 256
+ S7Object Partner;
+ int i;
+ for (i = 0; i < size; i++)
+bool ParLinked()
+ Par_GetStatus(Partner, &ParStatus);
+ return ParStatus==par_linked;
+void PrintError(int Error)
+ char text[1024];
+ Par_ErrorText(Partner, text, 1024);
+ printf("%s\n",text);
+ Partner = Par_Create(1);
+ Error=Par_StartTo(Partner, "0.0.0.0", argv[1], 0x1002, 0x1002);
+ PrintError(Error);
+ while (1)
+ while (!ParLinked())
+ SndError = Par_BSend(Partner, 0x00000001, &Buffer, size);
+ PrintError(SndError);
@@ -0,0 +1,530 @@
+ unsigned char Buffer[65536]; // 64 K buffer
+ int JobDone=false;
+int Check(int Result, char * function)
+ int ExecTime;
+ Cli_GetExecTime(Client, &ExecTime);
+ printf("| Execution time : %d ms\n",ExecTime);
+ Cli_ErrorText(Result, text, 1024);
+ printf("| %s\n",text);
+ return !Result;
+ int res;
+ res=Cli_ReadMultiVars(Client, &Items[0], 5);
+ int res=Cli_ListBlocks(Client, &List);
+ int res=Cli_GetOrderCode(Client, &Info);
+ int res=Cli_GetCpuInfo(Client, &Info);
+ printf(" Seriel Number : %s\n",Info.SerialNumber);
+ int res=Cli_GetCpInfo(Client, &Info);
+ Cli_GetPlcStatus(Client, &Status);
+ int res=Cli_Upload(Client, Block_SDB, 0, &Buffer, &Size);
+ res=Cli_AsUpload(Client, Block_SDB, 0, &Buffer, &Size);
+ res=Cli_WaitAsCompletion(Client,3000);
+ while (Cli_CheckAsCompletion(Client,&res)!=JobComplete)
+ PS7SZL SZL = (PS7SZL)(&Buffer); // use our buffer casted as TS7SZL
+ int res=Cli_ReadSZL(Client, 0x0011, 0x0000, SZL, &Size);
+int CliConnect()
+ int Requested, Negotiated, res;
+ res = Cli_ConnectTo(Client, Address,Rack,Slot);
+ Cli_GetPduLength(Client, &Requested, &Negotiated);
+ printf(" PDU Requested : %d bytes\n",Requested);
+ printf(" PDU Negotiated : %d bytes\n",Negotiated);
+ return !res;
+ Cli_Disconnect(Client);
+// Get Progran args
+ Cli_SetAsCallback(Client, CliCompletion,NULL);
@@ -0,0 +1,30 @@
+## LINUX barebone makefile for c examples : good for all platforms
+ $(CC) $(CFLAGS) -o client ../client.c $(Libs)
+ $(CC) $(CFLAGS) -o server ../server.c $(Libs)
+ $(CC) $(CFLAGS) -o srv_resourceless ../srv_resourceless.c $(Libs)
+ $(CC) $(CFLAGS) -o apartner ../apartner.c $(Libs)
+ $(CC) $(CFLAGS) -o ppartner ../ppartner.c $(Libs)
@@ -0,0 +1,171 @@
+ Partner = Par_Create(0);
+ Par_SetRecvCallback(Partner, RecvCallback, NULL);
+ Par_ErrorText(Error, text, 1024);
+ Par_Stop(Partner);
@@ -0,0 +1,114 @@
+ Srv_EventText(PEvent, text, 1024);
+ Server = Srv_Create();
+ Srv_RegisterArea(Server,
+ srvAreaDB, // We are registering a DB
+ Srv_RegisterArea(Server, srvAreaDB, 2, &DB2, sizeof(DB2));
+ Srv_RegisterArea(Server, srvAreaDB, 3, &DB3, sizeof(DB3));
+ Srv_SetEventsCallback(Server, EventCallBack, NULL);
+ // To select an adapter we have to use Srv_StartTo(Server, "192.168.x.y").
+ Error=Srv_Start(Server);
+ Srv_ErrorText(Error, text, 1024);
+ printf("%s\n", text);
+ Srv_Stop(Server); // <- not strictly needed, every server is stopped on deletion
@@ -0,0 +1,203 @@
+ //------------------------------------------------------------------------------
+ // Read/Write callback
+ int S7API RWAreaCallBack(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData)
+ PRWBuffer PBuffer = (PRWBuffer)pUsrData;
+ int c;
+ //case S7AreaPE: printf("Area : PE, ");
+ case 0x81: printf("Area : PE, ");
+ case 0x82: printf("Area : PA, ");
+ case 0x83: printf("Area : MK, ");
+ case 0x1C: printf("Area : CT, ");
+ case 0x1D: printf("Area : TM, ");
+ case 0x84: printf("Area : DB%d, ", PTag->DBNumber);
+ for (c = 0; c < 1024; c++)
+ Srv_SetMask(Server, mkEvent, 0x3ff);
+ Srv_SetRWAreaCallback(Server, RWAreaCallBack, NULL);
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+ Preamble
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+ The precise terms and conditions for copying, distribution and
+modification follow.
+ TERMS AND CONDITIONS
+ 0. Definitions.
+ "This License" refers to version 3 of the GNU General Public License.
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+ 1. Source Code.
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+ The Corresponding Source for a work in source code form is that
+same work.
+ 2. Basic Permissions.
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+ 4. Conveying Verbatim Copies.
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+ 5. Conveying Modified Source Versions.
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+ 6. Conveying Non-Source Forms.
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+ 7. Additional Terms.
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+ 8. Termination.
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+ 9. Acceptance Not Required for Having Copies.
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+ 10. Automatic Licensing of Downstream Recipients.
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+ 11. Patents.
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+ 12. No Surrender of Others' Freedom.
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+ 13. Use with the GNU Affero General Public License.
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+ 14. Revised Versions of this License.
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+ 15. Disclaimer of Warranty.
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ 16. Limitation of Liability.
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+ 17. Interpretation of Sections 15 and 16.
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+ END OF TERMS AND CONDITIONS
+ How to Apply These Terms to Your New Programs
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+Also add information on how to contact you by electronic and paper mail.
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+ GNU LESSER GENERAL PUBLIC LICENSE
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+ 0. Additional Definitions.
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+ 1. Exception to Section 3 of the GNU GPL.
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+ 2. Conveying Modified Versions.
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+ 3. Object Code Incorporating Material from Library Header Files.
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+ 4. Combined Works.
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+ d) Do one of the following:
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+ 5. Combined Libraries.
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+ 6. Revised Versions of the GNU Lesser General Public License.
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
@@ -0,0 +1,503 @@
+| PROJECT SNAP7 1.3.0 |
+| Copyright (C) 2013, 2015 Davide Nardella |
+#include "s7_client.h"
+TSnap7Client::TSnap7Client()
+ FThread = 0;
+ CliCompletion = 0;
+ EvtJob = NULL;
+ EvtComplete = NULL;
+ FThread=NULL;
+ ThreadCreated = false;
+TSnap7Client::~TSnap7Client()
+ Destroying=true;
+ Disconnect();
+ CliCompletion=NULL;
+ if (ThreadCreated)
+ CloseThread();
+ delete EvtComplete;
+ delete EvtJob;
+ ThreadCreated=false;
+void TSnap7Client::CloseThread()
+ int Timeout;
+ if (FThread)
+ FThread->Terminate();
+ if (Job.Pending)
+ Timeout=3000;
+ Timeout=1000;
+ EvtJob->Set();
+ if (FThread->WaitFor(Timeout)!=WAIT_OBJECT_0)
+ FThread->Kill();
+ try {
+ delete FThread;
+ catch (...){
+ FThread=0;
+void TSnap7Client::OpenThread()
+ FThread = new TClientThread(this);
+ FThread->Start();
+int TSnap7Client::Reset(bool DoReconnect)
+ bool WasConnected = Connected;
+ OpenThread();
+ if (DoReconnect || WasConnected)
+ return Connect();
+void TSnap7Client::DoCompletion()
+ if ((CliCompletion!=NULL) && !Destroying)
+ try{
+ CliCompletion(FUsrPtr, Job.Op, Job.Result);
+ }catch (...)
+int TSnap7Client::SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr)
+ CliCompletion=pCompletion;
+ FUsrPtr=usrPtr;
+int TSnap7Client::GetParam(int ParamNumber, void * pValue)
+ // Actually there are no specific client params, maybe in future...
+ return TSnap7MicroClient::GetParam(ParamNumber, pValue);
+int TSnap7Client::SetParam(int ParamNumber, void * pValue)
+ return TSnap7MicroClient::SetParam(ParamNumber, pValue);
+bool TSnap7Client::CheckAsCompletion(int &opResult)
+ if (!Job.Pending)
+ opResult=Job.Result;
+ if (!Destroying)
+ opResult=errCliJobPending; // don't set LastError here
+ opResult=errCliDestroying;
+ return true;
+ return !Job.Pending;
+int TSnap7Client::AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+ Job.Pending = true;
+ Job.Op = s7opReadArea;
+ Job.Area = Area;
+ Job.Number = DBNumber;
+ Job.Start = Start;
+ Job.Amount = Amount;
+ Job.WordLen = WordLen;
+ Job.pData = pUsrData;
+ JobStart = SysGetTick();
+ StartAsyncJob();
+ return SetError(errCliJobPending);
+int TSnap7Client::AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+ int ByteSize, TotalSize;
+ Job.Pending =true;
+ Job.Op =s7opWriteArea;
+ Job.Area =Area;
+ Job.Number =DBNumber;
+ Job.Start =Start;
+ // Performs some check first to copy the data
+ ByteSize=DataSizeByte(WordLen);
+ TotalSize=ByteSize*Amount; // Total size in bytes
+ if (ByteSize==0)
+ return SetError(errCliInvalidWordLen);
+ if ((TotalSize < 1) || (TotalSize > int(sizeof(opData))))
+ return SetError(errCliInvalidParams);
+ Job.Amount =Amount;
+ Job.WordLen =WordLen;
+ // Doublebuffering
+ memcpy(&opData, pUsrData, TotalSize);
+ Job.pData =&opData;
+ JobStart =SysGetTick();
+int TSnap7Client::AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount)
+ Job.Op =s7opListBlocksOfType;
+ Job.Area =BlockType;
+ Job.pData =pUsrData;
+ Job.pAmount =&ItemsCount;
+int TSnap7Client::AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size)
+ Job.Op =s7opReadSZL;
+ Job.ID =ID;
+ Job.Index =Index;
+ Job.pAmount =&Size;
+ Job.Amount =Size;
+ Job.IParam =1; // Data has to be copied into user buffer
+int TSnap7Client::AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount)
+ Job.Op =s7opReadSzlList;
+ Job.Amount =ItemsCount;
+int TSnap7Client::AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+ Job.Op =s7opUpload;
+ Job.Number =BlockNum;
+ Job.IParam =0; // not full upload, only data
+int TSnap7Client::AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+ Job.IParam =1; // full upload
+int TSnap7Client::AsDownload(int BlockNum, void * pUsrData, int Size)
+ // Checks the size : here we only need a size>0 to avoid problems during
+ // doublebuffering, the real test of the block size will be done in
+ // Checkblock.
+ if (Size<1)
+ return SetError(errCliInvalidBlockSize);
+ Job.Op =s7opDownload;
+ memcpy(&opData, pUsrData, Size);
+int TSnap7Client::AsCopyRamToRom(int Timeout)
+ Job.Op =s7opCopyRamToRom;
+ if (Timeout>0)
+ Job.IParam =Timeout;
+int TSnap7Client::AsCompress(int Timeout)
+ Job.Op =s7opCompress;
+int TSnap7Client::AsDBRead(int DBNumber, int Start, int Size, void * pUsrData)
+ return AsReadArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsDBWrite(int DBNumber, int Start, int Size, void * pUsrData)
+ return AsWriteArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsMBRead(int Start, int Size, void * pUsrData)
+ return AsReadArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsMBWrite(int Start, int Size, void * pUsrData)
+ return AsWriteArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsEBRead(int Start, int Size, void * pUsrData)
+ return AsReadArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsEBWrite(int Start, int Size, void * pUsrData)
+ return AsWriteArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsABRead(int Start, int Size, void * pUsrData)
+ return AsReadArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsABWrite(int Start, int Size, void * pUsrData)
+ return AsWriteArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7Client::AsTMRead(int Start, int Amount, void * pUsrData)
+ return AsReadArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
+int TSnap7Client::AsTMWrite(int Start, int Amount, void * pUsrData)
+ return AsWriteArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
+int TSnap7Client::AsCTRead(int Start, int Amount, void * pUsrData)
+ return AsReadArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
+int TSnap7Client::AsCTWrite(int Start, int Amount, void * pUsrData)
+ return AsWriteArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
+int TSnap7Client::AsDBGet(int DBNumber, void * pUsrData, int &Size)
+ if (Size<=0)
+ Job.Op =s7opDBGet;
+int TSnap7Client::AsDBFill(int DBNumber, int FillChar)
+ Job.Op =s7opDBFill;
+ Job.IParam =FillChar;
+void TSnap7Client::StartAsyncJob()
+ ClrError();
+ if (!ThreadCreated)
+ EvtJob = new TSnapEvent(false);
+ EvtComplete = new TSnapEvent(false);
+ ThreadCreated=true;
+ EvtComplete->Reset(); // reset if previously was not called WaitAsCompletion
+int TSnap7Client::WaitAsCompletion(unsigned long Timeout)
+ if (EvtComplete->WaitFor(Timeout)==WAIT_OBJECT_0)
+ return Job.Result;
+ if (Destroying)
+ return errCliDestroying;
+ return SetError(errCliJobTimeout);
+void TClientThread::Execute()
+ while (!Terminated)
+ FClient->EvtJob->WaitForever();
+ if (!Terminated)
+ FClient->PerformOperation();
+ FClient->EvtComplete->Set();
+ // Notify the caller the end of job (if callback is set)
+ FClient->DoCompletion();
@@ -0,0 +1,104 @@
+#ifndef s7_client_h
+#define s7_client_h
+#include "snap_threads.h"
+#include "s7_micro_client.h"
+typedef void (S7API *pfn_CliCompletion) (void * usrPtr, int opCode, int opResult);
+class TSnap7Client;
+class TClientThread: public TSnapThread
+ TSnap7Client * FClient;
+ TClientThread(TSnap7Client *Client)
+ FClient = Client;
+ void Execute();
+class TSnap7Client: public TSnap7MicroClient
+ TClientThread *FThread;
+ bool ThreadCreated;
+ void CloseThread();
+ void OpenThread();
+ void StartAsyncJob();
+protected:
+ PSnapEvent EvtJob;
+ PSnapEvent EvtComplete;
+ pfn_CliCompletion CliCompletion;
+ void *FUsrPtr;
+ void DoCompletion();
+ friend class TClientThread;
+ TSnap7Client();
+ ~TSnap7Client();
+ int Reset(bool DoReconnect);
+ int SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr);
+ bool CheckAsCompletion( int & opResult);
+ int WaitAsCompletion(unsigned long Timeout);
+ int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData);
+ int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData);
+ int AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount);
+ int AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size);
+ int AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount);
+ int AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size);
+ int AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size);
+ int AsDownload(int BlockNum, void * pUsrData, int Size);
+ int AsDBRead(int DBNumber, int Start, int Size, void * pUsrData);
+ int AsDBWrite(int DBNumber, int Start, int Size, void * pUsrData);
+ int AsMBRead(int Start, int Size, void * pUsrData);
+ int AsMBWrite(int Start, int Size, void * pUsrData);
+ int AsEBRead(int Start, int Size, void * pUsrData);
+ int AsEBWrite(int Start, int Size, void * pUsrData);
+ int AsABRead(int Start, int Size, void * pUsrData);
+ int AsABWrite(int Start, int Size, void * pUsrData);
+ int AsTMRead(int Start, int Amount, void * pUsrData);
+ int AsTMWrite(int Start, int Amount, void * pUsrData);
+ int AsCTRead(int Start, int Amount, void * pUsrData);
+ int AsCTWrite(int Start, int Amount, void * pUsrData);
+ int AsDBGet(int DBNumber, void * pUsrData, int & Size);
+typedef TSnap7Client *PSnap7Client;
+#endif // s7_client_h
@@ -0,0 +1,1256 @@
+#ifndef s7_firmware_h
+#define s7_firmware_h
+#include "snap_platform.h"
+// CPU DATABANK
+ byte SZLNotAvail[4] = {
+ 0x0A,0x00,0x00,0x00
+ byte SZLSysState[6] = {
+ 0xFF,0x09,0x00,0x02,0x02,0x00
+ byte SZL_ID_0000_IDX_XXXX[236] = {
+ 0xFF,0x09,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x70,0x00,0x00,0x0F,0x00,0x00,0x02,0x00,
+ 0x11,0x01,0x11,0x0F,0x11,0x00,0x12,0x01,0x12,0x0F,0x12,0x00,0x13,0x01,0x13,0x00,0x14,0x0F,0x14,
+ 0x00,0x15,0x01,0x15,0x00,0x17,0x01,0x17,0x0F,0x17,0x00,0x18,0x01,0x18,0x0F,0x18,0x00,0x19,0x0F,
+ 0x19,0x00,0x1A,0x0F,0x1A,0x00,0x1B,0x0F,0x1B,0x00,0x1C,0x01,0x1C,0x0F,0x1C,0x00,0x21,0x0A,0x21,
+ 0x0F,0x21,0x02,0x22,0x00,0x23,0x0F,0x23,0x00,0x24,0x01,0x24,0x04,0x24,0x05,0x24,0x00,0x25,0x01,
+ 0x25,0x02,0x25,0x0F,0x25,0x01,0x31,0x01,0x32,0x02,0x32,0x00,0x36,0x01,0x36,0x0F,0x36,0x00,0x37,
+ 0x01,0x37,0x0F,0x37,0x00,0x38,0x01,0x38,0x02,0x38,0x0F,0x38,0x01,0x39,0x00,0x3A,0x0F,0x3A,0x00,
+ 0x74,0x01,0x74,0x0F,0x74,0x05,0x91,0x0A,0x91,0x0C,0x91,0x0D,0x91,0x00,0x92,0x02,0x92,0x06,0x92,
+ 0x0F,0x92,0x00,0x94,0x01,0x94,0x02,0x94,0x06,0x94,0x07,0x94,0x0F,0x94,0x00,0x95,0x01,0x95,0x0F,
+ 0x95,0x06,0x96,0x0C,0x96,0x0C,0x97,0x0D,0x97,0x01,0x9A,0x02,0x9A,0x0F,0x9A,0x0C,0x9B,0x00,0x9C,
+ 0x01,0x9C,0x02,0x9C,0x03,0x9C,0x0F,0x9C,0x00,0xA0,0x01,0xA0,0x0F,0xA0,0x00,0xB1,0x00,0xB2,0x00,
+ 0xB3,0x00,0xB4,0x01,0xB5,0x02,0xB5,0x03,0xB5,0x04,0xB5,0x05,0xB5,0x06,0xB5,0x07,0xB5,0x08,0xB5,
+ 0x01,0xB6,0x02,0xB6,0x03,0xB6,0x04,0xB6
+ byte SZL_ID_0F00_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x00,0x00,0x00,0x00,0x02,0x00,0x70
+ byte SZL_ID_0002_IDX_XXXX[458] = { // <--Wrapped to 458 bytes
+ 0xFF,0x09,0x01,0xC6,0x00,0x02,0x00,0x00,0x00,0x08,0x00,0x71,0x01,0xAC,0x00,0x01,0x00,0x28,0x00,
+ 0x1C,0x01,0xAC,0x24,0x00,0x00,0x24,0x00,0x00,0x01,0xAC,0x23,0x00,0x00,0x06,0x04,0xB0,0x01,0xAC,
+ 0x22,0x00,0x00,0x08,0x00,0x01,0x01,0xAC,0x31,0x00,0x04,0x00,0x00,0x01,0x01,0xAC,0x12,0xFF,0x00,
+ 0x08,0x00,0x01,0x01,0xAC,0x12,0x31,0x00,0x08,0x00,0x01,0x01,0xAD,0x00,0x00,0x80,0x00,0x00,0x01,
+ 0x01,0xAD,0x01,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,
+ 0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x00,0x80,0x00,
+ 0x00,0x01,0x01,0xAD,0x06,0x00,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x00,0x80,0x00,0x00,0x01,0x01,
+ 0xAD,0x00,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x01,
+ 0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x01,0x80,0x00,0x00,
+ 0x01,0x01,0xAD,0x05,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,
+ 0x07,0x01,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x03,0x80,
+ 0x00,0x00,0x01,0x01,0xAD,0x02,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x03,0x80,0x00,0x00,0x01,
+ 0x01,0xAD,0x04,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x06,
+ 0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x03,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x04,0x80,0x00,
+ 0x00,0x01,0x01,0xAD,0x01,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x04,0x80,0x00,0x00,0x01,0x01,
+ 0xAD,0x03,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x04,
+ 0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x04,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x04,0x80,0x00,0x00,
+ 0x01,0x01,0xAD,0x00,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,
+ 0x02,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x04,0x05,0x80,
+ 0x00,0x00,0x01,0x01,0xAD,0x05,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x06,0x05,0x80,0x00,0x00,0x01,
+ 0x01,0xAD,0x07,0x05,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x01,
+ 0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x02,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x03,0x06,0x80,0x00,
+ 0x00,0x01,0x01,0xAD,0x04,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x05,0x06,0x80,0x00,0x00,0x01,0x01,
+ 0xAD,0x06,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x07,0x06,0x80,0x00,0x00,0x01,0x01,0xAD,0x00,0x07,
+ 0x80,0x00
+ byte SZL_ID_0011_IDX_XXXX[124] = {
+ 0xFF,0x09,0x00,0x78,0x00,0x11,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x01,0x36,0x45,0x53,0x37,0x20,
+ 0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04,
+ 0x00,0x01,0x00,0x06,0x36,0x45,0x53,0x37,0x20,0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,
+ 0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04,0x00,0x01,0x00,0x07,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xC0,0x56,0x03,0x02,
+ 0x06,0x00,0x81,0x42,0x6F,0x6F,0x74,0x20,0x4C,0x6F,0x61,0x64,0x65,0x72,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x00,0x00,0x41,0x20,0x09,0x09
+ byte SZL_ID_0012_IDX_XXXX[58] = {
+ 0xFF,0x09,0x00,0x36,0x00,0x12,0x00,0x00,0x00,0x02,0x00,0x17,0x00,0x01,0x01,0x01,0x01,0x04,0x03,
+ 0x02,0x03,0x03,0x03,0x04,0x03,0x06,0x03,0x07,0x03,0x08,0x03,0x09,0x03,0x0A,0x03,0x0B,0x03,0x0C,
+ 0x03,0x0D,0x03,0x0E,0x03,0x0F,0x03,0x10,0x03,0x11,0x03,0x12,0x03,0x13,0x03,0x14,0x03,0x15,0x03,
+ 0x17
+ byte SZL_ID_0013_IDX_XXXX[192] = {
+ 0xFF,0x09,0x00,0xBC,0x00,0x13,0x00,0x00,0x00,0x24,0x00,0x05,0x00,0x01,0x00,0x01,0x00,0x06,0x00,
+ 0x00,0x00,0x11,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x02,0x00,0x08,0x00,0x00,0x00,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,
+ 0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x02,0x00,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x02,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x20,0x00,0x00,
+ 0x00,0x00
+ byte SZL_ID_0014_IDX_XXXX[84] = {
+ 0xFF,0x09,0x00,0x50,0x00,0x14,0x00,0x00,0x00,0x08,0x00,0x09,0x00,0x01,0x00,0x01,0x08,0x00,0x00,
+ 0x00,0x00,0x02,0x00,0x01,0x08,0x00,0x00,0x00,0x00,0x03,0x00,0x01,0x40,0x00,0x00,0x80,0x00,0x04,
+ 0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x05,0x00,0x01,0x01,0x00,0x00,0x08,0x00,0x06,0x00,0x01,0x08,
+ 0x00,0x00,0x00,0x00,0x07,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x08,0x00,0x01,0x08,0x00,0x00,0x10,
+ 0x00,0x09,0x00,0x01,0x00,0x20,0x00,0x00
+ byte SZL_ID_0015_IDX_XXXX[62] = {
+ 0xFF,0x09,0x00,0x3A,0x00,0x15,0x00,0x00,0x00,0x0A,0x00,0x05,0x08,0x00,0x00,0x16,0x03,0xD1,0x00,
+ 0x00,0xFF,0xFE,0x0A,0x00,0x3E,0x81,0x03,0xD1,0x00,0x00,0xFF,0xFE,0x0B,0x00,0x04,0x22,0x03,0xD1,
+ 0x00,0x00,0xFF,0xFE,0x0C,0x00,0x1F,0x40,0x03,0xD1,0x00,0x00,0xFF,0xFE,0x0E,0x00,0x1F,0x40,0x03,
+ 0xD1,0x00,0x00,0xFF,0xFE
+ byte SZL_ID_0F14_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x14,0x00,0x00,0x00,0x08,0x00,0x09
+ byte SZL_ID_0019_IDX_XXXX[40] = {
+ 0xFF,0x09,0x00,0x24,0x00,0x19,0x00,0x00,0x00,0x04,0x00,0x07,0x00,0x01,0x00,0x00,0x00,0x04,0x01,
+ 0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x15,
+ byte SZL_ID_0F19_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x19,0x00,0x00,0x00,0x04,0x00,0x07
+ byte SZL_ID_001C_IDX_XXXX[352] = {
+ 0xFF,0x09,0x01,0x5C,0x00,0x1C,0x00,0x00,0x00,0x22,0x00,0x0A,0x00,0x01,0x53,0x4E,0x41,0x50,0x37,
+ 0x2D,0x53,0x45,0x52,0x56,0x45,0x52,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x43,0x50,0x55,0x20,0x33,0x31,0x35,0x2D,0x32,
+ 0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x04,0x4F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,0x53,0x69,0x65,0x6D,0x65,0x6E,0x73,0x20,
+ 0x45,0x71,0x75,0x69,0x70,0x6D,0x65,0x6E,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x53,0x20,
+ 0x43,0x2D,0x43,0x32,0x55,0x52,0x32,0x38,0x39,0x32,0x32,0x30,0x31,0x32,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x43,0x50,0x55,0x20,0x33,0x31,
+ 0x35,0x2D,0x32,0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x4D,0x4D,0x43,0x20,0x32,0x36,0x37,0x46,0x46,0x31,
+ 0x31,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x09,0x00,0x2A,0xF6,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0F1C_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x1C,0x00,0x00,0x00,0x22,0x00,0x0A
+ byte SZL_ID_0036_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0F36_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x36,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0025_IDX_XXXX[16] = {
+ 0xFF,0x09,0x00,0x0C,0x00,0x25,0x00,0x00,0x00,0x04,0x00,0x01,0x01,0x0C,0x3D,0x00
+ byte SZL_ID_0F25_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x25,0x00,0x00,0x00,0x04,0x00,0x01
+ byte SZL_ID_0037_IDX_XXXX[60] = {
+ 0xFF,0x09,0x00,0x38,0x00,0x37,0x00,0x00,0x00,0x30,0x00,0x01,0x07,0xFE,0xC0,0xA8,0x01,0x0A,0xFF,
+ 0xFF,0xFF,0x00,0xC0,0xA8,0x01,0x0A,0x00,0x1B,0x1B,0x1D,0x1A,0x2D,0x01,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x8F,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00
+ byte SZL_ID_0F37_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x37,0x00,0x00,0x00,0x30,0x00,0x01
+ byte SZL_ID_0074_IDX_XXXX[40] = {
+ 0xFF,0x09,0x00,0x24,0x00,0x74,0x00,0x00,0x00,0x04,0x00,0x07,0x00,0x01,0x00,0x00,0x00,0x04,0x01,
+ byte SZL_ID_0F74_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x74,0x00,0x00,0x00,0x04,0x00,0x07
+ byte SZL_ID_0591_IDX_XXXX[76] = {
+ 0xFF,0x09,0x00,0x48,0x05,0x91,0x00,0x00,0x00,0x10,0x00,0x04,0x00,0x00,0x02,0x01,0x07,0xFF,0xC4,
+ 0xC0,0xC4,0xC0,0x00,0x00,0xB4,0x02,0x00,0x11,0x00,0x00,0x02,0x02,0x07,0xFE,0xA7,0xC4,0xA7,0xC4,
+ 0x00,0x00,0xB4,0x02,0x00,0x11,0x00,0x00,0x02,0x03,0x07,0xFD,0x97,0xC5,0x97,0xC5,0x00,0x00,0xB4,
+ 0x02,0x00,0x11,0x00,0x00,0x02,0x04,0x07,0xFC,0x97,0xC5,0x97,0xC5,0x00,0x00,0xB4,0x02,0x00,0x11
+ byte SZL_ID_0A91_IDX_XXXX[44] = {
+ 0xFF,0x09,0x00,0x28,0x0A,0x91,0x00,0x00,0x00,0x10,0x00,0x02,0x01,0x00,0x02,0x01,0x07,0xFF,0xC4,
+ 0xC0,0xC4,0xC0,0x00,0x01,0xFE,0x02,0x00,0x11,0x80,0x00,0x00,0x00,0x07,0xFB,0xA7,0xC4,0xA7,0xC4,
+ 0x00,0x01,0xFE,0x02,0x00,0x11
+ byte SZL_ID_0F92_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x92,0x00,0x00,0x00,0x10,0x00,0x01
+ byte SZL_ID_0294_IDX_XXXX[270] = {
+ 0xFF,0x09,0x01,0x0A,0x02,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00
+ byte SZL_ID_0794_IDX_XXXX[270] = {
+ 0xFF,0x09,0x01,0x0A,0x07,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0F94_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x94,0x00,0x00,0x01,0x02,0x00,0x01
+ byte SZL_ID_0095_IDX_XXXX[52] = {
+ 0xFF,0x09,0x00,0x30,0x00,0x95,0x00,0x00,0x00,0x28,0x00,0x01,0x64,0x00,0x02,0x02,0x07,0xFE,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0F95_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x95,0x00,0x00,0x00,0x28,0x00,0x01
+ byte SZL_ID_00A0_IDX_XXXX[212] = {
+ 0xFF,0x09,0x00,0xD0,0x00,0xA0,0x00,0x00,0x00,0x14,0x00,0x0A,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x13,0x81,0xFE,0x64,0xC7,0x72,
+ 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x43,0x01,0xFF,0x46,0xC7,
+ 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF,
+ 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56,0x13,0x81,
+ 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46,0x43,
+ 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46,
+ 0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x51,0x19,0x48,
+ 0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,0x45,
+ 0x09,0x96,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x50,
+ 0x45,0x09,0x86
+ byte SZL_ID_0FA0_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0xA0,0x00,0x00,0x00,0x14,0x01,0xF4
+ byte SZL_ID_0017_IDX_XXXX[458] = { // <--Wrapped to 458 bytes
+ 0xFF,0x09,0x01,0xC6,0x00,0x17,0x00,0x00,0x00,0x04,0x00,0x73,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
+ 0x03,0x00,0x02,0x00,0x02,0x00,0x03,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x07,0x00,0x01,0x00,0x16,
+ 0x00,0x01,0x00,0x64,0x00,0x01,0x00,0x65,0x00,0x01,0x00,0x66,0x00,0x01,0x00,0x67,0x00,0x01,0x00,
+ 0x7A,0x00,0x01,0x00,0xC8,0x00,0x01,0x00,0xD2,0x00,0x01,0x02,0xBC,0x00,0x01,0x02,0xBD,0x00,0x01,
+ 0x02,0xBE,0x00,0x01,0x02,0xBF,0x00,0x01,0x02,0xC0,0x00,0x01,0x02,0xC1,0x00,0x01,0x02,0xC2,0x00,
+ 0x01,0x02,0xC3,0x00,0x01,0x02,0xC4,0x00,0x01,0x02,0xC5,0x00,0x01,0x02,0xC6,0x00,0x01,0x02,0xC7,
+ 0x00,0x01,0x02,0xC8,0x00,0x01,0x02,0xC9,0x00,0x01,0x02,0xCA,0x00,0x01,0x02,0xCB,0x00,0x01,0x02,
+ 0xCC,0x00,0x01,0x02,0xCD,0x00,0x01,0x02,0xCE,0x00,0x01,0x02,0xCF,0x00,0x01,0x02,0xD0,0x00,0x01,
+ 0x02,0xD1,0x00,0x01,0x02,0xD2,0x00,0x01,0x02,0xD3,0x00,0x01,0x02,0xD4,0x00,0x01,0x02,0xD5,0x00,
+ 0x01,0x02,0xD6,0x00,0x01,0x02,0xD7,0x00,0x01,0x02,0xD8,0x00,0x01,0x02,0xD9,0x00,0x01,0x02,0xDA,
+ 0x00,0x01,0x02,0xDB,0x00,0x01,0x02,0xDC,0x00,0x01,0x02,0xDD,0x00,0x01,0x02,0xDE,0x00,0x01,0x02,
+ 0xDF,0x00,0x01,0x02,0xE0,0x00,0x01,0x02,0xE1,0x00,0x01,0x02,0xE2,0x00,0x01,0x02,0xE3,0x00,0x01,
+ 0x02,0xE4,0x00,0x01,0x02,0xE5,0x00,0x01,0x02,0xE6,0x00,0x01,0x02,0xE7,0x00,0x01,0x02,0xE8,0x00,
+ 0x01,0x02,0xE9,0x00,0x01,0x02,0xEA,0x00,0x01,0x02,0xEB,0x00,0x01,0x02,0xEC,0x00,0x01,0x02,0xED,
+ 0x00,0x01,0x02,0xEE,0x00,0x01,0x02,0xEF,0x00,0x01,0x02,0xF0,0x00,0x01,0x02,0xF1,0x00,0x01,0x02,
+ 0xF2,0x00,0x01,0x02,0xF3,0x00,0x01,0x02,0xF4,0x00,0x01,0x02,0xF5,0x00,0x01,0x02,0xF6,0x00,0x01,
+ 0x02,0xF7,0x00,0x01,0x02,0xF8,0x00,0x01,0x02,0xF9,0x00,0x01,0x02,0xFA,0x00,0x01,0x02,0xFB,0x00,
+ 0x01,0x02,0xFC,0x00,0x01,0x02,0xFD,0x00,0x01,0x02,0xFE,0x00,0x01,0x02,0xFF,0x00,0x01,0x03,0x00,
+ 0x00,0x01,0x03,0x01,0x00,0x01,0x03,0x02,0x00,0x01,0x03,0x03,0x00,0x01,0x03,0x04,0x00,0x01,0x03,
+ 0x05,0x00,0x01,0x03,0x06,0x00,0x01,0x03,0x07,0x00,0x01,0x03,0x08,0x00,0x01,0x03,0x09,0x00,0x01,
+ 0x03,0x0A,0x00,0x01,0x03,0x0B,0x00,0x01,0x03,0x0C,0x00,0x01,0x03,0x0D,0x00,0x01,0x03,0x0E,0x00,
+ 0x01,0x03,0x0F,0x00,0x01,0x03,0x10,0x00,0x01,0x03,0x11,0x00,0x01,0x03,0x12,0x00,0x01,0x03,0x13,
+ 0x00,0x01,0x03,0x14,0x00,0x01,0x03,0x15,0x00,0x01,0x03,0x16,0x00,0x01,0x03,0x17,0x00,0x01,0x03,
+ 0x18,0x00,0x01,0x03,0x19,0x00,0x01,0x03,0x1A,0x00,0x01,0x03,0x1B,0x00,0x01,0x03,0x1C,0x00,0x01,
+ 0x03,0x1D
+ byte SZL_ID_0F17_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x17,0x00,0x00,0x00,0x04,0x00,0x73
+ byte SZL_ID_0018_IDX_XXXX[28] = {
+ 0xFF,0x09,0x00,0x18,0x00,0x18,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x01,0x00,
+ 0x08,0x00,0x02,0x00,0x08,0x00,0x03,0x00,0x08
+ byte SZL_ID_0F18_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x18,0x00,0x00,0x00,0x04,0x00,0x04
+ byte SZL_ID_001A_IDX_XXXX[48] = {
+ 0xFF,0x09,0x00,0x2C,0x00,0x1A,0x00,0x00,0x00,0x0C,0x00,0x03,0x09,0x01,0x01,0x05,0x00,0x08,0x00,
+ 0x00,0x00,0x80,0x00,0x00,0x12,0x01,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x18,0x01,
+ 0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00
+ byte SZL_ID_0F1A_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x1A,0x00,0x00,0x00,0x0C,0x00,0x03
+ byte SZL_ID_001B_IDX_XXXX[132] = {
+ 0xFF,0x09,0x00,0x80,0x00,0x1B,0x00,0x00,0x00,0x14,0x00,0x06,0x09,0x00,0x00,0x00,0x00,0x02,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x06,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x70,0x00,0x02,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xF3,0xFA,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x70,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xFF,0xDA,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,
+ 0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0F1B_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x1B,0x00,0x00,0x00,0x14,0x00,0x06
+ byte SZL_ID_0021_IDX_XXXX[100] = {
+ 0xFF,0x09,0x00,0x60,0x00,0x21,0x00,0x00,0x00,0x04,0x00,0x16,0x01,0x01,0x01,0x01,0x01,0x11,0xFE,
+ 0x0A,0x01,0x21,0xFE,0x14,0x01,0x22,0xFE,0x15,0x01,0x33,0xFE,0x20,0x01,0x34,0xFE,0x21,0x01,0x35,
+ 0xFE,0x22,0x01,0x36,0xFE,0x23,0x01,0x41,0xFE,0x28,0x01,0x55,0xFE,0x37,0x01,0x56,0xFE,0x38,0x01,
+ 0x57,0xFE,0x39,0x01,0x64,0xFE,0x3D,0x00,0x01,0xFE,0x50,0x00,0x42,0xFE,0x52,0x00,0x61,0xFE,0x53,
+ 0x00,0xA1,0xFE,0x55,0x00,0xC1,0xFE,0x56,0x00,0xD2,0xFE,0x57,0x00,0x81,0xFE,0x64,0x00,0x21,0xFE,
+ 0x79,0x00,0x42,0xFE,0x7A
+ byte SZL_ID_0A21_IDX_XXXX[16] = {
+ 0xFF,0x09,0x00,0x0C,0x0A,0x21,0x00,0x00,0x00,0x04,0x00,0x01,0x01,0x01,0x01,0x01
+ byte SZL_ID_0F21_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x21,0x00,0x00,0x00,0x04,0x00,0x16
+ byte SZL_ID_0023_IDX_XXXX[228] = {
+ 0xFF,0x09,0x00,0xE0,0x00,0x23,0x00,0x00,0x00,0x12,0x00,0x0C,0x1A,0x00,0x00,0x00,0x10,0xBE,0x00,
+ 0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x19,0x00,0x00,0x00,0x10,0x01,0x00,0x00,
+ 0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x10,0x00,0x00,0x00,0x10,0x26,0x00,0x00,0x08,
+ 0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0C,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,
+ 0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0B,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,
+ 0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x0A,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,
+ 0xFC,0x01,0xFF,0xFF,0xFF,0xF3,0x09,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,
+ 0x01,0xFF,0xFF,0xFF,0xF3,0x04,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,
+ 0xFF,0xFF,0xFF,0xF3,0x03,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,
+ 0xFF,0xFF,0xF3,0x02,0x00,0x00,0x00,0x10,0x16,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,
+ 0xFF,0xF3,0x01,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,
+ 0xF3,0x1B,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x08,0x00,0x0B,0xC0,0xFC,0x01,0xFF,0xFF,0xFF,0xF3
+ byte SZL_ID_0F23_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x23,0x00,0x00,0x00,0x12,0x00,0x0C
+ byte SZL_ID_0024_IDX_XXXX[92] = {
+ 0xFF,0x09,0x00,0x58,0x00,0x24,0x00,0x00,0x00,0x14,0x00,0x04,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,
+ 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66,0x43,0x04,0xFF,0x84,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16,0x43,0x02,0xFF,0x68,
+ 0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56
+ byte SZL_ID_0124_IDX_XXXX[32] = {
+ 0xFF,0x09,0x00,0x1C,0x01,0x24,0x00,0x00,0x00,0x14,0x00,0x01,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86
+ byte SZL_ID_0424_IDX_XXXX[32] = {
+ 0xFF,0x09,0x00,0x1C,0x04,0x24,0x00,0x00,0x00,0x14,0x00,0x01,0x51,0x44,0xFF,
+ 0x08, // <-- CPU Status
+ 0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x05,0x02,0x01,0x55,0x90,0x67
+ byte SZL_ID_0038_IDX_XXXX[78] = {
+ 0xFF,0x09,0x00,0x4A,0x00,0x38,0x00,0x00,0x00,0x42,0x00,0x01,0x00,0x01,0x07,0xFE,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAB,0xE3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xEE,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0F38_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x38,0x00,0x00,0x00,0x42,0x00,0x01
+ byte SZL_ID_003A_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x00,0x3A,0x00,0x00,0x00,0x94,0x00,0x00
+ byte SZL_ID_0F3A_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x3A,0x00,0x00,0x00,0x94,0x00,0x08
+ byte SZL_ID_0F9A_IDX_XXXX[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x9A,0x00,0x00,0x01,0x1C,0x00,0x01
+ byte SZL_ID_0D91_IDX_0000[28] = {
+ 0xFF,0x09,0x00,0x18,0x0D,0x91,0x00,0x00,0x00,0x10,0x00,0x01,0x00,0x00,0x02,0x00,0x7F,0xFF,0x00,
+ 0xC0,0x00,0xC0,0x00,0x00,0xB4,0x02,0x00,0x11
+ byte SZL_ID_0092_IDX_0000[28] = {
+ 0xFF,0x09,0x00,0x18,0x00,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0292_IDX_0000[28] = {
+ 0xFF,0x09,0x00,0x18,0x02,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0692_IDX_0000[28] = {
+ 0xFF,0x09,0x00,0x18,0x06,0x92,0x00,0x00,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0094_IDX_0000[270] = {
+ 0xFF,0x09,0x01,0x0A,0x00,0x94,0x00,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0D97_IDX_0000[60] = {
+ 0xFF,0x09,0x00,0x38,0x0D,0x97,0x00,0x00,0x00,0x30,0x00,0x01,0x7F,0xFF,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x02,0x00,0x04,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0111_IDX_0001[40] = {
+ 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x01,0x00,0x1C,0x00,0x01,0x00,0x01,0x36,0x45,0x53,0x37,0x20,
+ 0x00,0x01
+ byte SZL_ID_0111_IDX_0006[40] = {
+ 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x06,0x00,0x1C,0x00,0x01,0x00,0x06,0x36,0x45,0x53,0x37,0x20,
+ byte SZL_ID_0111_IDX_0007[40] = {
+ 0xFF,0x09,0x00,0x24,0x01,0x11,0x00,0x07,0x00,0x1C,0x00,0x01,0x00,0x07,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0xC0,0x56,0x03,
+ 0x02,0x06
+ byte SZL_ID_0F11_IDX_0001[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04
+ byte SZL_ID_0F11_IDX_0006[12] = {
+ byte SZL_ID_0F11_IDX_0007[12] = {
+ byte SZL_ID_0112_IDX_0000[14] = {
+ 0xFF,0x09,0x00,0x0A,0x01,0x12,0x00,0x00,0x00,0x02,0x00,0x01,0x00,0x01
+ byte SZL_ID_0112_IDX_0100[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x12,0x01,0x00,0x00,0x02,0x00,0x02,0x01,0x01,0x01,0x04
+ byte SZL_ID_0112_IDX_0200[12] = {
+ 0xFF,0x09,0x00,0x08,0x01,0x12,0x02,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0112_IDX_0400[12] = {
+ 0xFF,0x09,0x00,0x08,0x01,0x12,0x04,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0F12_IDX_0000[12] = {
+ 0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17
+ byte SZL_ID_0F12_IDX_0100[12] = {
+ byte SZL_ID_0F12_IDX_0200[12] = {
+ byte SZL_ID_0F12_IDX_0400[12] = {
+ byte SZL_ID_0113_IDX_0001[48] = {
+ 0xFF,0x09,0x00,0x2C,0x01,0x13,0x00,0x01,0x00,0x24,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x06,0x00,
+ byte SZL_ID_0115_IDX_0800[22] = {
+ 0xFF,0x09,0x00,0x12,0x01,0x15,0x08,0x00,0x00,0x0A,0x00,0x01,0x08,0x00,0x00,0x16,0x03,0xD1,0x00,
+ 0x00,0xFF,0xFE
+ byte SZL_ID_011C_IDX_0001[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x01,0x00,0x22,0x00,0x01,0x00,0x01,0x53,0x4D,0x41,0x52,0x54,
+ 0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_011C_IDX_0002[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x02,0x00,0x22,0x00,0x01,0x00,0x02,0x43,0x50,0x55,0x20,0x33,
+ 0x31,0x35,0x2D,0x32,0x20,0x50,0x4E,0x2F,0x44,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_011C_IDX_0003[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x03,0x00,0x22,0x00,0x01,0x00,0x03,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_011C_IDX_0004[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x04,0x00,0x22,0x00,0x01,0x00,0x04,0x4F,0x72,0x69,0x67,0x69,
+ 0x6E,0x61,0x6C,0x20,0x53,0x69,0x65,0x6D,0x65,0x6E,0x73,0x20,0x45,0x71,0x75,0x69,0x70,0x6D,0x65,
+ 0x6E,0x74,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_011C_IDX_0005[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x05,0x00,0x22,0x00,0x01,0x00,0x05,0x53,0x20,0x43,0x2D,0x43,
+ 0x32,0x55,0x52,0x32,0x38,0x39,0x32,0x32,0x30,0x31,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_011C_IDX_0007[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x07,0x00,0x22,0x00,0x01,0x00,0x07,0x43,0x50,0x55,0x20,0x33,
+ byte SZL_ID_011C_IDX_0008[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x08,0x00,0x22,0x00,0x01,0x00,0x08,0x4D,0x4D,0x43,0x20,0x32,
+ 0x36,0x37,0x46,0x46,0x31,0x31,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_011C_IDX_0009[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x09,0x00,0x22,0x00,0x01,0x00,0x09,0x00,0x2A,0xF6,0x00,0x00,
+ byte SZL_ID_011C_IDX_000A[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x0A,0x00,0x22,0x00,0x01,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_011C_IDX_000B[46] = {
+ 0xFF,0x09,0x00,0x2A,0x01,0x1C,0x00,0x0B,0x00,0x22,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0222_IDX_0001[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x01,0x00,0x1C,0x00,0x01,0x11,0x03,0x01,0x01,0xC8,0x58,0x00,
+ 0x00,0x00,0x00,0x00,0x01,0x94,0x02,0x05,0x02,0x01,0x56,0x64,0x77,0x00,0x10,0x00,0x08,0x00,0x00,
+ byte SZL_ID_0222_IDX_000A[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x0A,0x00,0x1C,0x00,0x01,0x10,0x11,0x02,0x0A,0x00,0x50,0x00,
+ byte SZL_ID_0222_IDX_0014[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x14,0x00,0x1C,0x00,0x01,0x10,0x21,0x03,0x14,0x00,0x00,0x00,
+ byte SZL_ID_0222_IDX_0028[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x28,0x00,0x1C,0x00,0x01,0x10,0x41,0x10,0x28,0x00,0x00,0x00,
+ byte SZL_ID_0222_IDX_0050[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x50,0x00,0x1C,0x00,0x01,0x35,0x01,0xFE,0x50,0xC8,0x58,0x00,
+ 0x00,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,
+ byte SZL_ID_0222_IDX_0064[40] = {
+ 0xFF,0x09,0x00,0x24,0x02,0x22,0x00,0x64,0x00,0x1C,0x00,0x01,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,
+ 0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76,0x00,0x00,0x00,0x08,0x00,0x00,
+ byte SZL_ID_0125_IDX_0000[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x25,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x03,0x01,0x00
+ byte SZL_ID_0125_IDX_0001[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x25,0x00,0x01,0x00,0x04,0x00,0x01,0x01,0x0C,0x3D,0x00
+ byte SZL_ID_0225_IDX_0001[16] = {
+ 0xFF,0x09,0x00,0x0C,0x02,0x25,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x03,0x01,0x00
+ byte SZL_ID_0132_IDX_0001[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03
+ byte SZL_ID_0132_IDX_0002[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x02,0x00,0x28,0x00,0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x06,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0132_IDX_0003[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x03,0x00,0x28,0x00,0x01,0x00,0x03,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0132_IDX_0004[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x00,0x00,
+ 0x01,0x00,0x02,0x00,0x00,0x00,0x00,0x56,0x56,0x10,0x01,0x33,0x7B,0x02,0x00,0x75,0xF4,0x02,0x00,
+ byte SZL_ID_0132_IDX_0005[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x05,0x00,0x28,0x00,0x01,0x00,0x05,0x00,0x00,0x00,0x01,0x00,
+ byte SZL_ID_0132_IDX_0006[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x06,0x00,0x28,0x00,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0132_IDX_0007[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x07,0x00,0x28,0x00,0x01,0x00,0x07,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0132_IDX_0008[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x08,0x00,0x28,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x05,0x02,
+ 0x01,0x56,0x94,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0132_IDX_0009[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x09,0x00,0x28,0x00,0x01,0x00,0x09,0x00,0x02,0xDC,0x6C,0x00,
+ byte SZL_ID_0132_IDX_000A[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0A,0x00,0x28,0x00,0x01,0x00,0x0A,0x00,0x02,0xDC,0x6C,0x00,
+ byte SZL_ID_0132_IDX_000B[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0B,0x00,0x28,0x00,0x01,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0132_IDX_000C[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x32,0x00,0x0C,0x00,0x28,0x00,0x01,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0232_IDX_0001[52] = {
+ 0xFF,0x09,0x00,0x30,0x02,0x32,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ byte SZL_ID_0232_IDX_0004[52] = {
+ 0xFF,0x09,0x00,0x30,0x02,0x32,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x00,0x00,
+ byte SZL_ID_0137_IDX_07FE[60] = {
+ 0xFF,0x09,0x00,0x38,0x01,0x37,0x07,0xFE,0x00,0x30,0x00,0x01,0x07,0xFE,0xC0,0xA8,0x01,0x0A,0xFF,
+ byte SZL_ID_0174_IDX_0001[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x00
+ byte SZL_ID_0174_IDX_0004[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x04,0x00,0x04,0x00,0x01,0x00,0x04,0x01,0x00
+ byte SZL_ID_0174_IDX_0005[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x05,0x00,0x04,0x00,0x01,0x00,0x05,0x00,0x00
+ byte SZL_ID_0174_IDX_0006[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x06,0x00,0x04,0x00,0x01,0x00,0x06,0x00,0x00
+ byte SZL_ID_0174_IDX_000B[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x0B,0x00,0x04,0x00,0x01,0x00,0x0B,0x00,0x00
+ byte SZL_ID_0174_IDX_000C[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x74,0x00,0x0C,0x00,0x04,0x00,0x01,0x00,0x0C,0x00,0x00
+ byte SZL_ID_0194_IDX_0064[270] = {
+ 0xFF,0x09,0x01,0x0A,0x01,0x94,0x00,0x64,0x01,0x02,0x00,0x01,0x00,0x64,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0694_IDX_0064[270] = {
+ 0xFF,0x09,0x01,0x0A,0x06,0x94,0x00,0x64,0x01,0x02,0x00,0x01,0x00,0x64,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_01A0_IDX_0000[12] = {
+ 0xFF,0x09,0x00,0x08,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x00
+ byte SZL_ID_01A0_IDX_0001[32] = {
+ 0xFF,0x09,0x00,0x1C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x01,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ byte SZL_ID_01A0_IDX_0002[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x02,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x76
+ byte SZL_ID_01A0_IDX_0003[72] = {
+ 0xFF,0x09,0x00,0x44,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x03,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x29,0x66
+ byte SZL_ID_01A0_IDX_0004[92] = {
+ 0xFF,0x09,0x00,0x58,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x04,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x52,0x13,0x90,0x16
+ byte SZL_ID_01A0_IDX_0005[112] = {
+ 0xFF,0x09,0x00,0x6C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x05,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x56
+ byte SZL_ID_01A0_IDX_0006[132] = {
+ 0xFF,0x09,0x00,0x80,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x06,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x88,0x46
+ byte SZL_ID_01A0_IDX_0007[152] = {
+ 0xFF,0x09,0x00,0x94,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x07,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x51,0x21,0x79,0x46
+ byte SZL_ID_01A0_IDX_0008[172] = {
+ 0xFF,0x09,0x00,0xA8,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x08,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x26
+ byte SZL_ID_01A0_IDX_0009[192] = {
+ 0xFF,0x09,0x00,0xBC,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x09,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x09,0x96
+ byte SZL_ID_01A0_IDX_000A[212] = {
+ 0xFF,0x09,0x00,0xD0,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0A,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ byte SZL_ID_01A0_IDX_000B[232] = {
+ 0xFF,0x09,0x00,0xE4,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0B,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x45,0x09,0x86,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+ 0x50,0x45,0x00,0x96
+ byte SZL_ID_01A0_IDX_000C[252] = {
+ 0xFF,0x09,0x00,0xF8,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0C,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+ 0x23,0x50,0x42,0x54,0x66
+ byte SZL_ID_01A0_IDX_000D[272] = {
+ 0xFF,0x09,0x01,0x0C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0D,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x23,0x50,0x42,0x54,0x66,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,
+ 0x04,0x23,0x50,0x32,0x40,0x46
+ byte SZL_ID_01A0_IDX_000E[292] = {
+ 0xFF,0x09,0x01,0x20,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0E,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x04,0x23,0x50,0x32,0x40,0x46,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+ 0x02,0x04,0x23,0x50,0x32,0x40,0x36
+ byte SZL_ID_01A0_IDX_000F[312] = {
+ 0xFF,0x09,0x01,0x34,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0F,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x02,0x04,0x23,0x50,0x32,0x40,0x36,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,
+ 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46
+ byte SZL_ID_01A0_IDX_0010[332] = {
+ 0xFF,0x09,0x01,0x48,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x10,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x94,0x02,0x04,0x23,0x50,0x32,0x31,0x46,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26
+ byte SZL_ID_01A0_IDX_0011[352] = {
+ 0xFF,0x09,0x01,0x5C,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x11,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x94,0x02,0x04,0x23,0x50,0x29,0x92,0x26,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,0x00,0x08,0x14,
+ 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66
+ byte SZL_ID_01A0_IDX_0012[372] = {
+ 0xFF,0x09,0x01,0x70,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x12,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x66,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,
+ 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56
+ byte SZL_ID_01A0_IDX_0013[392] = {
+ 0xFF,0x09,0x01,0x84,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x13,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x26,0x56,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,
+ 0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x17,0x66
+ byte SZL_ID_01A0_IDX_0014[412] = {
+ 0xFF,0x09,0x01,0x98,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x14,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x49,0x22,0x17,0x66,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x49,0x19,0x75,0x46
+ byte SZL_ID_01A0_IDX_0015[432] = {
+ 0xFF,0x09,0x01,0xAC,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x15,0x43,0x02,0xFF,0x68,0xC7,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,0x23,0x49,0x19,0x75,0x46,0x43,0x02,0xFF,0x68,0xC7,0x00,
+ 0x00,0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x48,0x32,0x21,0x16
+ byte SZL_ID_0117_IDX_0000[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x01
+ byte SZL_ID_0117_IDX_0001[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x03
+ byte SZL_ID_0117_IDX_0002[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x02,0x00,0x04,0x00,0x01,0x00,0x02,0x00,0x02
+ byte SZL_ID_0117_IDX_0003[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x03,0x00,0x01
+ byte SZL_ID_0117_IDX_0004[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x17,0x00,0x04,0x00,0x04,0x00,0x01,0x00,0x04,0x00,0x01
+ byte SZL_ID_0118_IDX_0000[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x00,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x08
+ byte SZL_ID_0118_IDX_0001[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x01,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x08
+ byte SZL_ID_0118_IDX_0002[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x02,0x00,0x04,0x00,0x01,0x00,0x02,0x00,0x08
+ byte SZL_ID_0118_IDX_0003[16] = {
+ 0xFF,0x09,0x00,0x0C,0x01,0x18,0x00,0x03,0x00,0x04,0x00,0x01,0x00,0x03,0x00,0x08
+ byte SZL_ID_0131_IDX_0001[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x01,0x00,0x28,0x00,0x01,0x00,0x01,
+ 0x08,0x00, // PDU SIZE : We expose 2048 00F0
+ 0x04,0x00, // Max Commections : We expose 1024 0010
+ 0x00,0xB7,0x1B,0x00,0x00,0x02,0xDC,0x6C,0x05,0xF5,0xE1,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0131_IDX_0002[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x02,0x00,0x28,0x00,0x01,0x00,0x02,0xBE,0xFD,0x4F,0x00,0x00,
+ 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3C,0x01,0x08,0x00,0x00,0x00,0x7D,0x00,0x00,0x05,0x03,0x0F,
+ 0x00,0x00,0x08,0x00,0x00,0x0C,0x00,0x0A,0x00,0x00,0x00,0x01,0x00,0x00
+ byte SZL_ID_0131_IDX_0003[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x03,0x00,0x28,0x00,0x01,0x00,0x03,0x7F,0xFC,0x83,0x01,
+ 0x00,0xF0, // Max size of consistently readable data (will be set = PDU size)
+ 0x00,0x10,0x00,0x01,0x02,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0131_IDX_0004[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x04,0x00,0x28,0x00,0x01,0x00,0x04,0xFE,0x01,0x62,0x41,0x63,
+ 0x00,0x1E,0x00,0x10,0x10,0x10,0x04,0x02,0x00,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0131_IDX_0005[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x05,0x00,0x28,0x00,0x01,0x00,0x05,0x3E,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x10,0x01,0xF4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0131_IDX_0006[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x06,0x00,0x28,0x00,0x01,0x00,0x06,0xF3,0x00,0x00,0xF8,0x01,
+ 0xF0,0xFF,0x4E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,
+ 0x01,0x00,0x00,0x0E,0x00,0x4C,0xFF,0xFF,0x05,0xC0,0x00,0x00,0x00,0x00
+ byte SZL_ID_0131_IDX_0007[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x07,0x00,0x28,0x00,0x01,0x00,0x07,0x01,0x00,0x3F,0x00,0x20,
+ 0x01,0x01,0x00,0x00,0x00,0x01,0x08,0x01,0x08,0x01,0x08,0x20,0x08,0x02,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0131_IDX_0008[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x08,0x00,0x28,0x00,0x01,0x00,0x08,0x70,0x03,0x70,0x02,0x70,
+ 0x02,0x40,0x32,0x40,0x32,0x40,0x32,0x40,0x64,0x40,0x32,0x40,0x32,0x40,0x64,0x40,0x64,0x40,0x64,
+ 0x40,0x64,0x40,0x64,0x40,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ byte SZL_ID_0131_IDX_0009[52] = {
+ 0xFF,0x09,0x00,0x30,0x01,0x31,0x00,0x09,0x00,0x28,0x00,0x01,0x00,0x09,0x04,0x06,0x01,0x00,0x01,
+ 0xF7,0x01,0xF7,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ byte SZL_ID_0C91_IDX_07FE[28] = {
+ 0xFF,0x09,0x00,0x18,0x0C,0x91,0x07,0xFE,0x00,0x10,0x00,0x01,0x00,0x00,0x02,0x02,0x07,0xFE,0xA7,
+ 0xC4,0xA7,0xC4,0x00,0x00,0xB4,0x02,0x00,0x11
@@ -0,0 +1,541 @@
+#include "s7_isotcp.h"
+TIsoTcpSocket::TIsoTcpSocket()
+ RecvTimeout = 3000; // Some old equipments are a bit slow to answer....
+ RemotePort = isoTcpPort;
+ // These fields should be $0000 and in any case RFC says that they are not considered.
+ // But some equipment...need a non zero value for the source reference.
+ DstRef = 0x0000;
+ SrcRef = 0x0100;
+ // PDU size requested
+ IsoPDUSize =1024;
+ IsoMaxFragments=MaxIsoFragments;
+ LastIsoError=0;
+TIsoTcpSocket::~TIsoTcpSocket()
+int TIsoTcpSocket::CheckPDU(void *pPDU, u_char PduTypeExpected)
+ PIsoHeaderInfo Info;
+ ClrIsoError();
+ if (pPDU!=0)
+ Info = PIsoHeaderInfo(pPDU);
+ Size = PDUSize(pPDU);
+ // Performs check
+ if (( Size<7 ) || ( Size>IsoPayload_Size ) || // Checks RFC 1006 header length
+ ( Info->HLength<sizeof( TCOTP_DT )-1 ) || // Checks ISO 8073 header length
+ ( Info->PDUType!=PduTypeExpected)) // Checks PDU Type
+ return SetIsoError(errIsoInvalidPDU);
+ return noError;
+ return SetIsoError(errIsoNullPointer);
+int TIsoTcpSocket::SetIsoError(int Error)
+ LastIsoError = Error | LastTcpError;
+ return LastIsoError;
+void TIsoTcpSocket::ClrIsoError()
+ LastTcpError=0;
+int TIsoTcpSocket::BuildControlPDU()
+ int ParLen, IsoLen;
+ FControlPDU.COTP.Params.PduSizeCode=0xC0; // code that identifies TPDU size
+ FControlPDU.COTP.Params.PduSizeLen =0x01; // 1 byte this field
+ switch(IsoPDUSize)
+ case 128:
+ FControlPDU.COTP.Params.PduSizeVal =0x07;
+ case 256:
+ FControlPDU.COTP.Params.PduSizeVal =0x08;
+ case 512:
+ FControlPDU.COTP.Params.PduSizeVal =0x09;
+ case 1024:
+ FControlPDU.COTP.Params.PduSizeVal =0x0A;
+ case 2048:
+ FControlPDU.COTP.Params.PduSizeVal =0x0B;
+ case 4096:
+ FControlPDU.COTP.Params.PduSizeVal =0x0C;
+ case 8192:
+ FControlPDU.COTP.Params.PduSizeVal =0x0D;
+ default:
+ FControlPDU.COTP.Params.PduSizeVal =0x0B; // Our Default
+ // Build TSAPs
+ FControlPDU.COTP.Params.TSAP[0]=0xC1; // code that identifies source TSAP
+ FControlPDU.COTP.Params.TSAP[1]=2; // source TSAP Len
+ FControlPDU.COTP.Params.TSAP[2]=(SrcTSap>>8) & 0xFF; // HI part
+ FControlPDU.COTP.Params.TSAP[3]=SrcTSap & 0xFF; // LO part
+ FControlPDU.COTP.Params.TSAP[4]=0xC2; // code that identifies dest TSAP
+ FControlPDU.COTP.Params.TSAP[5]=2; // dest TSAP Len
+ FControlPDU.COTP.Params.TSAP[6]=(DstTSap>>8) & 0xFF; // HI part
+ FControlPDU.COTP.Params.TSAP[7]=DstTSap & 0xFF; // LO part
+ // Params length
+ ParLen=11; // 2 Src TSAP (Code+field Len) +
+ // 2 Src TSAP len +
+ // 2 Dst TSAP (Code+field Len) +
+ // 3 PDU size (Code+field Len+Val) = 11
+ // Telegram length
+ IsoLen=sizeof(TTPKT)+ // TPKT Header
+ 7 + // COTP Header Size without params
+ ParLen; // COTP params
+ FControlPDU.TPKT.Version =isoTcpVersion;
+ FControlPDU.TPKT.Reserved =0;
+ FControlPDU.TPKT.HI_Lenght=0; // Connection Telegram size cannot exced 255 bytes, so
+ // this field is always 0
+ FControlPDU.TPKT.LO_Lenght=IsoLen;
+ FControlPDU.COTP.HLength =ParLen + 6; // <-- 6 = 7 - 1 (COTP Header size - 1)
+ FControlPDU.COTP.PDUType =pdu_type_CR; // Connection Request
+ FControlPDU.COTP.DstRef =DstRef; // Destination reference
+ FControlPDU.COTP.SrcRef =SrcRef; // Source reference
+ FControlPDU.COTP.CO_R =0x00; // Class + Option : RFC0983 states that it must be always 0x40
+ // but for some equipment (S7) must be 0 in disaccord of specifications !!!
+int TIsoTcpSocket::PDUSize(void *pPDU)
+ return PIsoHeaderInfo(pPDU)->TPKT.HI_Lenght*256+PIsoHeaderInfo( pPDU )->TPKT.LO_Lenght;
+void TIsoTcpSocket::IsoParsePDU(TIsoControlPDU pdu)
+// Currently we accept a connection with any kind of src/dst tsap
+// Override to implement special filters.
+int TIsoTcpSocket::IsoConfirmConnection(u_char PDUType)
+ PIsoControlPDU CPDU = PIsoControlPDU(&PDU);
+ u_short TempRef;
+ PDU.COTP.PDUType=PDUType;
+ // Exchange SrcRef<->DstRef, not strictly needed by COTP 8073 but S7PLC as client needs it.
+ TempRef=CPDU->COTP.DstRef;
+ CPDU->COTP.DstRef=CPDU->COTP.SrcRef;
+ CPDU->COTP.SrcRef=0x0100;//TempRef;
+ return SendPacket(&PDU,PDUSize(&PDU));
+void TIsoTcpSocket::FragmentSkipped(int Size)
+// override for log purpose
+int TIsoTcpSocket::isoConnect()
+ pbyte TmpControlPDU;
+ PIsoControlPDU ControlPDU;
+ u_int Length;
+ // Build the default connection telegram
+ BuildControlPDU();
+ ControlPDU =&FControlPDU;
+ // Checks the format
+ Result =CheckPDU(ControlPDU, pdu_type_CR);
+ if (Result!=0)
+ Result =SckConnect();
+ if (Result==noError)
+ // Calcs the length
+ Length =PDUSize(ControlPDU);
+ // Send connection telegram
+ SendPacket(ControlPDU, Length);
+ if (LastTcpError==0)
+ TmpControlPDU = pbyte(ControlPDU);
+ // Receives TPKT header (4 bytes)
+ RecvPacket(TmpControlPDU, sizeof(TTPKT));
+ // Calc the packet length
+ Length =PDUSize(TmpControlPDU);
+ // Check if it fits in the buffer and if it's greater then TTPKT size
+ if ((Length<=sizeof(TIsoControlPDU)) && (Length>sizeof(TTPKT)))
+ // Points to COTP
+ TmpControlPDU+=sizeof(TTPKT);
+ Length -= sizeof(TTPKT);
+ // Receives remainin bytes 4 bytes after
+ RecvPacket(TmpControlPDU, Length);
+ // Finally checks the Connection Confirm telegram
+ Result =CheckPDU(ControlPDU, pdu_type_CC);
+ LastIsoError=Result;
+ Result =SetIsoError(errIsoRecvPacket);
+ Result =SetIsoError(errIsoInvalidPDU);
+ // Flush buffer
+ Purge();
+ Result =SetIsoError(errIsoSendPacket);
+ SckDisconnect();
+int TIsoTcpSocket::isoSendBuffer(void *Data, int Size)
+ u_int IsoSize;
+ // Total Size = Size + Header Size
+ IsoSize =Size+DataHeaderSize;
+ // Checks the length
+ if ((IsoSize>0) && (IsoSize<=IsoFrameSize))
+ // Builds the header
+ Result =0;
+ // TPKT
+ PDU.TPKT.Version = isoTcpVersion;
+ PDU.TPKT.Reserved = 0;
+ PDU.TPKT.HI_Lenght= (u_short(IsoSize)>> 8) & 0xFF;
+ PDU.TPKT.LO_Lenght= u_short(IsoSize) & 0xFF;
+ // COPT
+ PDU.COTP.HLength =sizeof(TCOTP_DT)-1;
+ PDU.COTP.PDUType =pdu_type_DT;
+ PDU.COTP.EoT_Num =pdu_EoT;
+ // Fill payload
+ if (Data!=0) // Data=null ==> use internal buffer PDU.Payload
+ memcpy(&PDU.Payload, Data, Size);
+ // Send over TCP/IP
+ SendPacket(&PDU, IsoSize);
+ if (LastTcpError!=0)
+ Result =SetIsoError(errIsoInvalidDataSize );
+int TIsoTcpSocket::isoRecvBuffer(void *Data, int & Size)
+ Size =0;
+ Result =isoRecvPDU(&PDU);
+ Size =PDUSize( &PDU )-DataHeaderSize;
+ if (Data!=0) // Data=NULL ==> a child will consume directly PDY.Payload
+ memcpy(Data, &PDU.Payload, Size);
+int TIsoTcpSocket::isoExchangeBuffer(void *Data, int &Size)
+ Result =isoSendBuffer(Data, Size);
+ Result =isoRecvBuffer(Data, Size);
+bool TIsoTcpSocket::IsoPDUReady()
+ return PacketReady(sizeof(TCOTP_DT));
+int TIsoTcpSocket::isoDisconnect(bool OnlyTCP)
+ if (Connected)
+ Purge(); // Flush pending
+ // OnlyTCP true -> Disconnect Request telegram is not required : only TCP disconnection
+ if (!OnlyTCP)
+ // if we are connected -> we have a valid connection telegram
+ FControlPDU.COTP.PDUType =pdu_type_DR;
+ Result =CheckPDU(&FControlPDU, pdu_type_DR);
+ // Sends Disconnect request
+ SendPacket(&FControlPDU, PDUSize(&FControlPDU));
+ // TCP disconnect
+ Result =SetIsoError(errIsoDisconnect);
+int TIsoTcpSocket::isoSendPDU(PIsoDataPDU Data)
+ Result=CheckPDU(Data,pdu_type_DT);
+ SendPacket(Data,PDUSize(Data));
+ Result=SetIsoError(errIsoSendPacket);
+int TIsoTcpSocket::isoRecvFragment(void *From, int Max, int &Size, bool &EoT)
+ int DataLength;
+ EoT =false;
+ byte PDUType;
+ // header is received always from beginning
+ RecvPacket(&PDU, DataHeaderSize); // TPKT + COPT_DT
+ PDUType=PDU.COTP.PDUType;
+ switch (PDUType)
+ case pdu_type_CR:
+ case pdu_type_DR:
+ EoT=true;
+ case pdu_type_DT:
+ EoT = (PDU.COTP.EoT_Num & 0x80) == 0x80; // EoT flag
+ DataLength = PDUSize(&PDU) - DataHeaderSize;
+ if (CheckPDU(&PDU, PDUType)!=0)
+ // Checks for data presence
+ if (DataLength>0) // payload present
+ // Check if the data fits in the buffer
+ if(DataLength<=Max)
+ RecvPacket(From, DataLength);
+ return SetIsoError(errIsoRecvPacket);
+ Size =DataLength;
+ return SetIsoError(errIsoPduOverflow);
+// Fragments Recv schema
+//
+// packet 1 packet 2 packet 3
+// +--------+------------+ +--------+------------+ +--------+------------+
+// | HEADER | FRAGMENT 1 | | HEADER | FRAGMENT 2 | | HEADER | FRAGMENT 3 |
+// | | |
+// | +-----------+ |
+// | | +------------------------+
+// | | | (Packet 3 has EoT Flag set)
+// V V V
+// +--------+------------+------------+------------+
+// | HEADER | FRAGMENT 1 : FRAGMENT 2 : FRAGMENT 3 |
+// ^
+// |
+// +-- A new header is built with updated info
+int TIsoTcpSocket::isoRecvPDU(PIsoDataPDU Data)
+ pbyte pData;
+ int max;
+ int Offset;
+ int Received;
+ int NumParts;
+ bool Complete;
+ NumParts =1;
+ Offset =0;
+ Complete =false;
+ pData = pbyte(&PDU.Payload);
+ do {
+ pData=pData+Offset;
+ max =IsoPayload_Size-Offset; // Maximum packet allowed
+ if (max>0)
+ Result =isoRecvFragment(pData, max, Received, Complete);
+ if((Result==0) && !Complete)
+ ++NumParts;
+ Offset += Received;
+ if (NumParts>IsoMaxFragments)
+ Result =SetIsoError(errIsoTooManyFragments);
+ } while ((!Complete) && (Result==0));
+ // Add to offset the header size
+ Size =Offset+Received+DataHeaderSize;
+ // Adjust header
+ PDU.TPKT.HI_Lenght =(u_short(Size)>>8) & 0xFF;
+ PDU.TPKT.LO_Lenght =u_short(Size) & 0xFF;
+ // Copies data if target is not the local PDU
+ if (Data!=&PDU)
+ memcpy(Data, &PDU, Size);
+ if (LastTcpError!=WSAECONNRESET)
+int TIsoTcpSocket::isoExchangePDU(PIsoDataPDU Data)
+ Result=isoSendPDU(Data);
+ Result=isoRecvPDU(Data);
+void TIsoTcpSocket::IsoPeek(void *pPDU, TPDUKind &PduKind)
+ u_int IsoLen;
+ Info=PIsoHeaderInfo(pPDU);
+ IsoLen=PDUSize(Info);
+ // Check for empty fragment : size of PDU = size of header and nothing else
+ if (IsoLen==DataHeaderSize )
+ // We don't need to check the EoT flag since the PDU is empty....
+ PduKind=pkEmptyFragment;
+ return;
+ // Check for invalid packet : size of PDU < size of header
+ if (IsoLen<DataHeaderSize )
+ PduKind=pkInvalidPDU;
+ // Here IsoLen>DataHeaderSize : check the PDUType
+ switch (Info->PDUType)
+ PduKind=pkConnectionRequest;
+ PduKind=pkDisconnectRequest;
+ PduKind=pkValidData;
+ PduKind=pkUnrecognizedType;
@@ -0,0 +1,271 @@
+#ifndef s7_isotcp_h
+#define s7_isotcp_h
+#include "snap_msgsock.h"
+#define isoTcpVersion 3 // RFC 1006
+#define isoTcpPort 102 // RFC 1006
+#define isoInvalidHandle 0
+#define MaxTSAPLength 16 // Max Lenght for Src and Dst TSAP
+#define MaxIsoFragments 64 // Max fragments
+#define IsoPayload_Size 4096 // Iso telegram Buffer size
+#define noError 0
+const longword errIsoMask = 0x000F0000;
+const longword errIsoBase = 0x0000FFFF;
+const longword errIsoInvalidDataSize = 0x00040000; // Bad Datasize passed to send/recv : buffer is invalid
+const longword ISO_OPT_TCP_NODELAY = 0x00000001; // Disable Nagle algorithm
+const longword ISO_OPT_INSIDE_MTU = 0x00000002; // Max packet size < MTU ethernet card
+// TPKT Header - ISO on TCP - RFC 1006 (4 bytes)
+ u_char Version; // Always 3 for RFC 1006
+ u_char Reserved; // 0
+ u_char HI_Lenght; // High part of packet lenght (entire frame, payload and TPDU included)
+ u_char LO_Lenght; // Low part of packet lenght (entire frame, payload and TPDU included)
+} TTPKT; // Packet length : min 7 max 65535
+ u_char PduSizeCode;
+ u_char PduSizeLen;
+ u_char PduSizeVal;
+ u_char TSAP[245]; // We don't know in advance these fields....
+} TCOPT_Params ;
+// PDU Type constants - ISO 8073, not all are mentioned in RFC 1006
+// For our purposes we use only those labeled with **
+// These constants contains 4 low bit order 0 (credit nibble)
+// $10 ED : Expedited Data
+// $20 EA : Expedited Data Ack
+// $40 UD : CLTP UD
+// $50 RJ : Reject
+// $70 AK : Ack data
+// ** $80 DR : Disconnect request (note : S7 doesn't use it)
+// ** $C0 DC : Disconnect confirm (note : S7 doesn't use it)
+// ** $D0 CC : Connection confirm
+// ** $E0 CR : Connection request
+// ** $F0 DT : Data
+// COTP Header for CONNECTION REQUEST/CONFIRM - DISCONNECT REQUEST/CONFIRM
+ u_char HLength; // Header length : initialized to 6 (length without params - 1)
+ // descending classes that add values in params field must update it.
+ u_char PDUType; // 0xE0 Connection request
+ // 0xD0 Connection confirm
+ // 0x80 Disconnect request
+ // 0xDC Disconnect confirm
+ u_short DstRef; // Destination reference : Always 0x0000
+ u_short SrcRef; // Source reference : Always 0x0000
+ u_char CO_R; // If the telegram is used for Connection request/Confirm,
+ // the meaning of this field is CLASS+OPTION :
+ // Class (High 4 bits) + Option (Low 4 bits)
+ // Class : Always 4 (0100) but is ignored in input (RFC States this)
+ // Option : Always 0, also this in ignored.
+ // If the telegram is used for Disconnect request,
+ // the meaning of this field is REASON :
+ // 1 Congestion at TSAP
+ // 2 Session entity not attached to TSAP
+ // 3 Address unknown (at TCP connect time)
+ // 128+0 Normal disconnect initiated by the session
+ // entity.
+ // 128+1 Remote transport entity congestion at connect
+ // request time
+ // 128+3 Connection negotiation failed
+ // 128+5 Protocol Error
+ // 128+8 Connection request refused on this network
+ // connection
+ // Parameter data : depending on the protocol implementation.
+ // ISO 8073 define several type of parameters, but RFC 1006 recognizes only
+ // TSAP related parameters and PDU size. See RFC 0983 for more details.
+ TCOPT_Params Params;
+ /* Other params not used here, list only for completeness
+ ACK_TIME = 0x85, 1000 0101 Acknowledge Time
+ RES_ERROR = 0x86, 1000 0110 Residual Error Rate
+ PRIORITY = 0x87, 1000 0111 Priority
+ TRANSIT_DEL = 0x88, 1000 1000 Transit Delay
+ THROUGHPUT = 0x89, 1000 1001 Throughput
+ SEQ_NR = 0x8A, 1000 1010 Subsequence Number (in AK)
+ REASSIGNMENT = 0x8B, 1000 1011 Reassignment Time
+ FLOW_CNTL = 0x8C, 1000 1100 Flow Control Confirmation (in AK)
+ TPDU_SIZE = 0xC0, 1100 0000 TPDU Size
+ SRC_TSAP = 0xC1, 1100 0001 TSAP-ID / calling TSAP ( in CR/CC )
+ DST_TSAP = 0xC2, 1100 0010 TSAP-ID / called TSAP
+ CHECKSUM = 0xC3, 1100 0011 Checksum
+ VERSION_NR = 0xC4, 1100 0100 Version Number
+ PROTECTION = 0xC5, 1100 0101 Protection Parameters (user defined)
+ OPT_SEL = 0xC6, 1100 0110 Additional Option Selection
+ PROTO_CLASS = 0xC7, 1100 0111 Alternative Protocol Classes
+ PREF_MAX_TPDU_SIZE = 0xF0, 1111 0000
+ INACTIVITY_TIMER = 0xF2, 1111 0010
+ ADDICC = 0xe0 1110 0000 Additional Information on Connection Clearing
+ */
+} TCOTP_CO ;
+typedef TCOTP_CO *PCOTP_CO;
+// COTP Header for DATA EXCHANGE
+ u_char HLength; // Header length : 3 for this header
+ u_char PDUType; // 0xF0 for this header
+ u_char EoT_Num; // EOT (bit 7) + PDU Number (bits 0..6)
+ // EOT = 1 -> End of Trasmission Packet (This packet is complete)
+ // PDU Number : Always 0
+} TCOTP_DT;
+typedef TCOTP_DT *PCOTP_DT;
+// Info part of a PDU, only common parts. We use it to check the consistence
+// of a telegram regardless of it's nature (control or data).
+ TTPKT TPKT; // TPKT Header
+ // Common part of any COTP
+ u_char PDUType; // Pdu type
+} TIsoHeaderInfo ;
+typedef TIsoHeaderInfo *PIsoHeaderInfo;
+// PDU Type consts (Code + Credit)
+const byte pdu_type_CR = 0xE0; // Connection request
+const byte pdu_type_CC = 0xD0; // Connection confirm
+const byte pdu_type_DR = 0x80; // Disconnect request
+const byte pdu_type_DC = 0xC0; // Disconnect confirm
+const byte pdu_type_DT = 0xF0; // Data transfer
+const byte pdu_EoT = 0x80; // End of Trasmission Packet (This packet is complete)
+const longword DataHeaderSize = sizeof(TTPKT)+sizeof(TCOTP_DT);
+const longword IsoFrameSize = IsoPayload_Size+DataHeaderSize;
+ TCOTP_CO COTP; // COPT Header for CONNECTION stuffs
+} TIsoControlPDU;
+typedef TIsoControlPDU *PIsoControlPDU;
+typedef u_char TIsoPayload[IsoPayload_Size];
+ TCOTP_DT COTP; // COPT Header for DATA EXCHANGE
+ TIsoPayload Payload; // Payload
+} TIsoDataPDU ;
+typedef TIsoDataPDU *PIsoDataPDU;
+typedef TIsoPayload *PIsoPayload;
+typedef enum {
+ pkConnectionRequest,
+ pkDisconnectRequest,
+ pkEmptyFragment,
+ pkInvalidPDU,
+ pkUnrecognizedType,
+ pkValidData
+} TPDUKind ;
+void ErrIsoText(int Error, char *Msg, int len);
+class TIsoTcpSocket : public TMsgSocket
+ TIsoControlPDU FControlPDU;
+ int IsoMaxFragments; // max fragments allowed for an ISO telegram
+ // Checks the PDU format
+ int CheckPDU(void *pPDU, u_char PduTypeExpected);
+ // Receives the next fragment
+ int isoRecvFragment(void *From, int Max, int &Size, bool &EoT);
+ TIsoDataPDU PDU;
+ int SetIsoError(int Error);
+ // Builds the control PDU starting from address properties
+ virtual int BuildControlPDU();
+ // Calcs the PDU size
+ int PDUSize(void *pPDU);
+ // Parses the connection request PDU to extract TSAP and PDU size info
+ virtual void IsoParsePDU(TIsoControlPDU PDU);
+ // Confirms the connection, override this method for special pourpose
+ // By default it checks the PDU format and resend it changing the pdu type
+ int IsoConfirmConnection(u_char PDUType);
+ void ClrIsoError();
+ virtual void FragmentSkipped(int Size);
+ word SrcTSap; // Source TSAP
+ word DstTSap; // Destination TSAP
+ word SrcRef; // Source Reference
+ word DstRef; // Destination Reference
+ int IsoPDUSize;
+ int LastIsoError;
+ //--------------------------------------------------------------------------
+ TIsoTcpSocket();
+ ~TIsoTcpSocket();
+ // HIGH Level functions (work on payload hiding the underlying protocol)
+ // Connects with a peer, the connection PDU is automatically built starting from address scheme (see below)
+ int isoConnect();
+ // Disconnects from a peer, if OnlyTCP = true, only a TCP disconnect is performed,
+ // otherwise a disconnect PDU is built and send.
+ int isoDisconnect(bool OnlyTCP);
+ // Sends a buffer, a valid header is created
+ int isoSendBuffer(void *Data, int Size);
+ // Receives a buffer
+ int isoRecvBuffer(void *Data, int & Size);
+ // Exchange cycle send->receive
+ int isoExchangeBuffer(void *Data, int & Size);
+ // A PDU is ready (at least its header) to be read
+ bool IsoPDUReady();
+ // Same as isoSendBuffer, but the entire PDU has to be provided (in any case a check is performed)
+ int isoSendPDU(PIsoDataPDU Data);
+ // Same as isoRecvBuffer, but it returns the entire PDU, automatically enques the fragments
+ int isoRecvPDU(PIsoDataPDU Data);
+ // Same as isoExchangeBuffer, but the entire PDU has to be provided (in any case a check is performed)
+ int isoExchangePDU(PIsoDataPDU Data);
+ // Peeks an header info to know which kind of telegram is incoming
+ void IsoPeek(void *pPDU, TPDUKind &PduKind);
+#endif // s7_isotcp_h
@@ -0,0 +1,3328 @@
+TSnap7MicroClient::TSnap7MicroClient()
+ SrcRef =0x0100; // RFC0983 states that SrcRef and DetRef should be 0
+ // and, in any case, they are ignored.
+ // S7 instead requires a number != 0
+ // Libnodave uses 0x0100
+ // S7Manager uses 0x0D00
+ // TIA Portal V12 uses 0x1D00
+ // WinCC uses 0x0300
+ // Seems that every non zero value is good enough...
+ DstRef =0x0000;
+ SrcTSap =0x0100;
+ DstTSap =0x0000; // It's filled by connection functions
+ ConnectionType = CONNTYPE_PG; // Default connection type
+ memset(&Job,0,sizeof(TSnap7Job));
+TSnap7MicroClient::~TSnap7MicroClient()
+ Destroying = true;
+int TSnap7MicroClient::opReadArea()
+ PReqFunReadParams ReqParams;
+ PResFunReadParams ResParams;
+ PS7ResHeader23 Answer;
+ PResFunReadItem ResData;
+ word RPSize; // ReqParams size
+ int WordSize;
+ uintptr_t Offset;
+ pbyte Target;
+ int Address;
+ int IsoSize;
+ int MaxElements; // Max elements that we can transfer in a PDU
+ word NumElements; // Num of elements that we are asking for this telegram
+ int TotElements; // Total elements requested
+ WordSize=DataSizeByte(Job.WordLen); // The size in bytes of an element that we are asking for
+ if (WordSize==0)
+ return errCliInvalidWordLen;
+ // First check : params bounds
+ if ((Job.Number<0) || (Job.Number>65535) || (Job.Start<0) || (Job.Amount<1))
+ return errCliInvalidParams;
+ // Second check : transport size
+ if ((Job.WordLen==S7WLBit) && (Job.Amount>1))
+ return errCliInvalidTransportSize;
+ // Request Params size
+ RPSize =sizeof(TReqFunReadItem)+2; // 1 item + FunRead + ItemsCount
+ // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+ ReqParams =PReqFunReadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ Answer =PS7ResHeader23(&PDU.Payload);
+ ResParams =PResFunReadParams(pbyte(Answer)+ResHeaderSize23);
+ ResData =PResFunReadItem(pbyte(ResParams)+sizeof(TResFunReadParams));
+ // Each packet cannot exceed the PDU length (in bytes) negotiated, and moreover
+ // we must ensure to transfer a "finite" number of item per PDU
+ MaxElements=(PDULength-sizeof(TS7ResHeader23)-sizeof(TResFunReadParams)-4) / WordSize;
+ TotElements=Job.Amount;
+ Start =Job.Start;
+ while ((TotElements>0) && (Result==0))
+ NumElements=TotElements;
+ if (NumElements>MaxElements)
+ NumElements=MaxElements;
+ Target=pbyte(Job.pData)+Offset;
+ //----------------------------------------------- Read next slice-----
+ PDUH_out->P = 0x32; // Always 0x32
+ PDUH_out->PDUType = PduType_request; // 0x01
+ PDUH_out->AB_EX = 0x0000; // Always 0x0000
+ PDUH_out->Sequence = GetNextWord(); // AutoInc
+ PDUH_out->ParLen = SwapWord(RPSize); // 14 bytes params
+ PDUH_out->DataLen = 0x0000; // No data
+ ReqParams->FunRead = pduFuncRead; // 0x04
+ ReqParams->ItemsCount = 1;
+ ReqParams->Items[0].ItemHead[0] = 0x12;
+ ReqParams->Items[0].ItemHead[1] = 0x0A;
+ ReqParams->Items[0].ItemHead[2] = 0x10;
+ ReqParams->Items[0].TransportSize = Job.WordLen;
+ ReqParams->Items[0].Length = SwapWord(NumElements);
+ ReqParams->Items[0].Area = Job.Area;
+ if (Job.Area==S7AreaDB)
+ ReqParams->Items[0].DBNumber = SwapWord(Job.Number);
+ ReqParams->Items[0].DBNumber = 0x0000;
+ // Adjusts the offset
+ if ((Job.WordLen==S7WLBit) || (Job.WordLen==S7WLCounter) || (Job.WordLen==S7WLTimer))
+ Address = Start;
+ Address = Start*8;
+ ReqParams->Items[0].Address[2] = Address & 0x000000FF;
+ Address = Address >> 8;
+ ReqParams->Items[0].Address[1] = Address & 0x000000FF;
+ ReqParams->Items[0].Address[0] = Address & 0x000000FF;
+ IsoSize = sizeof(TS7ReqHeader)+RPSize;
+ Result = isoExchangeBuffer(0,IsoSize);
+ // Get Data
+ if (Result==0) // 1St level Iso
+ Size = 0;
+ // Item level error
+ if (ResData->ReturnCode==0xFF) // <-- 0xFF means Result OK
+ // Calcs data size in bytes
+ Size = SwapWord(ResData->DataLength);
+ // Adjust Size in accord of TransportSize
+ if ((ResData->TransportSize != TS_ResOctet) && (ResData->TransportSize != TS_ResReal) && (ResData->TransportSize != TS_ResBit))
+ Size = Size >> 3;
+ memcpy(Target, &ResData->Data[0], Size);
+ Result = CpuError(ResData->ReturnCode);
+ Offset+=Size;
+ //--------------------------------------------------------------------
+ TotElements -= NumElements;
+ Start += NumElements*WordSize;
+int TSnap7MicroClient::opWriteArea()
+ PReqFunWriteParams ReqParams;
+ PReqFunWriteDataItem ReqData; // only 1 item for WriteArea Function
+ PResFunWrite ResParams;
+ word RHSize; // Request headers size
+ bool First = true;
+ pbyte Source;
+ word Size;
+ uintptr_t Offset = 0;
+ int Start; // where we are starting from for this telegram
+ int Result = 0;
+ WordSize=DataSizeByte(Job.WordLen); // The size in bytes of an element that we are pushing
+ RHSize =sizeof(TS7ReqHeader)+ // Request header
+ 2+ // FunWrite+ItemCount (of TReqFunWriteParams)
+ sizeof(TReqFunWriteItem)+// 1 item reference
+ 4; // ReturnCode+TransportSize+DataLength
+ RPSize =sizeof(TReqFunWriteItem)+2;
+ ReqParams=PReqFunWriteParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqData =PReqFunWriteDataItem(pbyte(ReqParams)+sizeof(TReqFunWriteItem)+2); // 2 = FunWrite+ItemsCount
+ Target =pbyte(ReqData)+4; // 4 = ReturnCode+TransportSize+DataLength
+ ResParams=PResFunWrite(pbyte(Answer)+ResHeaderSize23);
+ MaxElements=(PDULength-RHSize) / WordSize;
+ Source=pbyte(Job.pData)+Offset;
+ Size=NumElements * WordSize;
+ PDUH_out->P=0x32; // Always 0x32
+ PDUH_out->PDUType=PduType_request; // 0x01
+ PDUH_out->AB_EX=0x0000; // Always 0x0000
+ PDUH_out->Sequence=GetNextWord(); // AutoInc
+ PDUH_out->ParLen =SwapWord(RPSize); // 14 bytes params
+ PDUH_out->DataLen =SwapWord(Size+4);
+ ReqParams->FunWrite=pduFuncWrite; // 0x05
+ ReqParams->ItemsCount=1;
+ ReqParams->Items[0].ItemHead[0]=0x12;
+ ReqParams->Items[0].ItemHead[1]=0x0A;
+ ReqParams->Items[0].ItemHead[2]=0x10;
+ ReqParams->Items[0].TransportSize=Job.WordLen;
+ ReqParams->Items[0].Length=SwapWord(NumElements);
+ ReqParams->Items[0].Area=Job.Area;
+ ReqParams->Items[0].DBNumber=SwapWord(Job.Number);
+ ReqParams->Items[0].DBNumber=0x0000;
+ Address=Start;
+ Address=Start*8;
+ ReqParams->Items[0].Address[2]=Address & 0x000000FF;
+ Address=Address >> 8;
+ ReqParams->Items[0].Address[1]=Address & 0x000000FF;
+ ReqParams->Items[0].Address[0]=Address & 0x000000FF;
+ ReqData->ReturnCode=0x00;
+ switch(Job.WordLen)
+ case S7WLBit:
+ ReqData->TransportSize=TS_ResBit;
+ case S7WLInt:
+ case S7WLDInt:
+ ReqData->TransportSize=TS_ResInt;
+ case S7WLReal:
+ ReqData->TransportSize=TS_ResReal;
+ case S7WLChar :
+ case S7WLCounter:
+ case S7WLTimer:
+ ReqData->TransportSize=TS_ResOctet;
+ ReqData->TransportSize=TS_ResByte;
+ if ((ReqData->TransportSize!=TS_ResOctet) && (ReqData->TransportSize!=TS_ResReal) && (ReqData->TransportSize!=TS_ResBit))
+ ReqData->DataLength=SwapWord(Size*8);
+ ReqData->DataLength=SwapWord(Size);
+ memcpy(Target, Source, Size);
+ IsoSize=RHSize + Size;
+ Result=isoExchangeBuffer(0,IsoSize);
+ if (Result==0) // 1St check : Iso result
+ Result=CpuError(SwapWord(Answer->Error)); // 2nd level global error
+ { // 2th check : item error
+ if (ResParams->Data[0] == 0xFF) // <-- 0xFF means Result OK
+ Result=0;
+ // Now we check the error : if it's the first part we report the cpu error
+ // otherwise we warn that the function failed but some data were written
+ if (First)
+ Result=CpuError(ResParams->Data[0]);
+ Result=errCliPartialDataWritten;
+ First=false;
+ TotElements-=NumElements;
+ Start+=(NumElements*WordSize);
+int TSnap7MicroClient::opReadMultiVars()
+ PS7DataItem Item;
+ TResFunReadData ResData;
+ uintptr_t Offset =0 ;
+ word Slice;
+ longword Address;
+ pbyte P;
+ int ItemsCount, c, Result;
+ Item = PS7DataItem(Job.pData);
+ ItemsCount = Job.Amount;
+ // Some useful initial check to detail the errors (Since S7 CPU always answers
+ // with $05 if (something is wrong in params)
+ if (ItemsCount>MaxVars)
+ return errCliTooManyItems;
+ // Adjusts Word Length in case of timers and counters and clears results
+ for (c = 0; c < ItemsCount; c++)
+ Item->Result=0;
+ if (Item->Area==S7AreaCT)
+ Item->WordLen=S7WLCounter;
+ if (Item->Area==S7AreaTM)
+ Item->WordLen=S7WLTimer;
+ Item++;
+ // Let's build the PDU
+ RPSize = word(2 + ItemsCount * sizeof(TReqFunReadItem));
+ ReqParams = PReqFunReadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ Answer = PS7ResHeader23(&PDU.Payload);
+ ResParams = PResFunReadParams(pbyte(Answer)+ResHeaderSize23);
+ // Fill Header
+ PDUH_out->ParLen=SwapWord(RPSize); // Request params size
+ PDUH_out->DataLen=0x0000; // No data in output
+ // Fill Params
+ ReqParams->FunRead=pduFuncRead; // 0x04
+ ReqParams->ItemsCount=ItemsCount;
+ ReqParams->Items[c].ItemHead[0]=0x12;
+ ReqParams->Items[c].ItemHead[1]=0x0A;
+ ReqParams->Items[c].ItemHead[2]=0x10;
+ ReqParams->Items[c].TransportSize=Item->WordLen;
+ ReqParams->Items[c].Length=SwapWord(Item->Amount);
+ ReqParams->Items[c].Area=Item->Area;
+ // Automatically drops DBNumber if (Area is not DB
+ if (Item->Area==S7AreaDB)
+ ReqParams->Items[c].DBNumber=SwapWord(Item->DBNumber);
+ ReqParams->Items[c].DBNumber=0x0000;
+ if ((Item->WordLen==S7WLBit) || (Item->WordLen==S7WLCounter) || (Item->WordLen==S7WLTimer))
+ Address=Item->Start;
+ Address=Item->Start*8;
+ // Builds the offset
+ ReqParams->Items[c].Address[2]=Address & 0x000000FF;
+ ReqParams->Items[c].Address[1]=Address & 0x000000FF;
+ ReqParams->Items[c].Address[0]=Address & 0x000000FF;
+ IsoSize=RPSize+sizeof(TS7ReqHeader);
+ if (IsoSize>PDULength)
+ return errCliSizeOverPDU;
+ // Function level error
+ if (Answer->Error!=0)
+ return CpuError(SwapWord(Answer->Error));
+ if (ResParams->ItemCount!=ItemsCount)
+ return errCliInvalidPlcAnswer;
+ P=pbyte(ResParams)+sizeof(TResFunReadParams);
+ ResData[c] =PResFunReadItem(pbyte(P)+Offset);
+ Slice=0;
+ if (ResData[c]->ReturnCode==0xFF) // <-- 0xFF means Result OK
+ Slice=SwapWord(ResData[c]->DataLength);
+ if ((ResData[c]->TransportSize != TS_ResOctet) && (ResData[c]->TransportSize != TS_ResReal) && (ResData[c]->TransportSize != TS_ResBit))
+ Slice=Slice >> 3;
+ memcpy(Item->pdata, ResData[c]->Data, Slice);
+ Item->Result=CpuError(ResData[c]->ReturnCode);
+ if ((Slice % 2)!=0)
+ Slice++; // Skip fill byte for Odd frame
+ Offset+=(4+Slice);
+int TSnap7MicroClient::opWriteMultiVars()
+ TReqFunWriteData ReqData;
+ int ItemsCount, c, IsoSize;
+ word Size; // Write data size
+ int WordSize, Result;
+ // Let's build the PDU : setup pointers
+ RPSize = word(2 + ItemsCount * sizeof(TReqFunWriteItem));
+ ReqParams = PReqFunWriteParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams = PResFunWrite(pbyte(Answer)+ResHeaderSize23);
+ P=pbyte(ReqParams)+RPSize;
+ Offset=0;
+ // Items Params
+ // Items Data
+ ReqData[c]=PReqFunWriteDataItem(pbyte(P)+Offset);
+ ReqData[c]->ReturnCode=0x00;
+ switch (Item->WordLen)
+ case S7WLBit :
+ ReqData[c]->TransportSize=TS_ResBit;
+ case S7WLInt :
+ case S7WLDInt :
+ ReqData[c]->TransportSize=TS_ResInt;
+ case S7WLReal :
+ ReqData[c]->TransportSize=TS_ResReal;
+ case S7WLCounter :
+ case S7WLTimer : ReqData[c]->TransportSize=TS_ResOctet;
+ default :
+ ReqData[c]->TransportSize=TS_ResByte; // byte/word/dword etc.
+ WordSize=DataSizeByte(Item->WordLen);
+ Size=Item->Amount * WordSize;
+ if ((ReqData[c]->TransportSize!=TS_ResOctet) && (ReqData[c]->TransportSize!=TS_ResReal) && (ReqData[c]->TransportSize!=TS_ResBit))
+ ReqData[c]->DataLength=SwapWord(Size*8);
+ ReqData[c]->DataLength=SwapWord(Size);
+ memcpy(ReqData[c]->Data, Item->pdata, Size);
+ if ((Size % 2) != 0 && (ItemsCount - c != 1))
+ Size++; // Skip fill byte for Odd frame (except for the last one)
+ Offset+=(4+Size); // next item
+ PDUH_out->DataLen=SwapWord(word(Offset));
+ IsoSize=RPSize+sizeof(TS7ReqHeader)+int(Offset);
+ if (ResParams->Data[c]==0xFF) // <-- 0xFF means Result OK
+ Item->Result=CpuError(ResParams->Data[c]);
+int TSnap7MicroClient::opListBlocks()
+ PReqFunGetBlockInfo ReqParams;
+ PReqDataFunBlocks ReqData;
+ PResFunGetBlockInfo ResParams;
+ PDataFunListAll ResData;
+ PS7ResHeader17 Answer;
+ PS7BlocksList List;
+ int IsoSize, Result;
+ ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqData =PReqDataFunBlocks(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo));
+ Answer =PS7ResHeader17(&PDU.Payload);
+ ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17);
+ ResData =PDataFunListAll(pbyte(ResParams)+sizeof(TResFunGetBlockInfo));
+ List =PS7BlocksList(Job.pData);
+ PDUH_out->PDUType=PduType_userdata; // 0x07
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunGetBlockInfo)); // 8 bytes params
+ PDUH_out->DataLen=SwapWord(sizeof(TReqDataFunBlocks)); // 4 bytes data
+ // Fill params (mostly constants)
+ ReqParams->Head[0]=0x00;
+ ReqParams->Head[1]=0x01;
+ ReqParams->Head[2]=0x12;
+ ReqParams->Plen =0x04;
+ ReqParams->Uk =0x11;
+ ReqParams->Tg =grBlocksInfo;
+ ReqParams->SubFun =SFun_ListAll;
+ ReqParams->Seq =0x00;
+ // Fill data
+ ReqData[0] =0x0A;
+ ReqData[1] =0x00;
+ ReqData[2] =0x00;
+ ReqData[3] =0x00;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunGetBlockInfo)+sizeof(TReqDataFunBlocks);
+ if (ResParams->ErrNo==0)
+ if (SwapWord(ResData->Length)!=28)
+ for (int c = 0; c < 7; c++)
+ switch (ResData->Blocks[c].BType)
+ case Block_OB:
+ List->OBCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_DB:
+ List->DBCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_SDB:
+ List->SDBCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_FC:
+ List->FCCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_SFC:
+ List->SFCCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_FB:
+ List->FBCount=SwapWord(ResData->Blocks[c].BCount);
+ case Block_SFB:
+ List->SFBCount=SwapWord(ResData->Blocks[c].BCount);
+ Result=CpuError(SwapWord(ResParams->ErrNo));
+int TSnap7MicroClient::opListBlocksOfType()
+ PReqDataBlockOfType ReqData;
+ PDataFunGetBot ResData;
+ longword *PadData;
+ word *List;
+ bool First;
+ bool Done = false;
+ byte BlockType, In_Seq;
+ int Count, Last, IsoSize, Result;
+ int c, CThis;
+ word DataLength;
+ bool RoomError = false;
+ BlockType=Job.Area;
+ List=(word*)(&opData);
+ ResData =PDataFunGetBot(pbyte(ResParams)+sizeof(TResFunGetBlockInfo));
+ First =true;
+ In_Seq=0x00; // first group sequence, next will come from PLC
+ Count =0;
+ Last =0;
+ //<--------------------------------------------------------- Get next slice
+ PDUH_out->ParLen=SwapWord(8); // 8 bytes params
+ PDUH_out->DataLen=SwapWord(6); // 6 bytes data
+ DataLength=14;
+ PDUH_out->ParLen=SwapWord(12); // 12 bytes params
+ PDUH_out->DataLen=SwapWord(4); // 4 bytes data
+ DataLength=16;
+ ReqParams->Plen =0x08;
+ ReqParams->Uk = 0x11;
+ ReqParams->Uk = 0x12;
+ ReqParams->SubFun =SFun_ListBoT;
+ ReqParams->Seq =In_Seq;
+ // overlap resvd and error to avoid another struct...
+ ReqData =PReqDataBlockOfType(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo));
+ ReqData->RetVal =0xFF;
+ ReqData->TSize =TS_ResOctet;
+ ReqData->Length =SwapWord(0x0002);
+ ReqData->Zero =0x30; // zero ascii '0'
+ ReqData->BlkType =BlockType;
+ PadData =(longword*)(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo));
+ ReqData =PReqDataBlockOfType(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo)+4);
+ *PadData =0x00000000;
+ ReqData->RetVal =0x0A;
+ ReqData->TSize =0x00;
+ ReqData->Length =0x0000;
+ ReqData->Zero =0x00;
+ ReqData->BlkType =0x00;
+ IsoSize=sizeof(TS7ReqHeader)+DataLength;
+ if (ResData->RetVal==0xFF)
+ Done=((ResParams->Rsvd & 0xFF00) == 0); // Low order byte = 0x00 => the sequence is done
+ In_Seq=ResParams->Seq; // every next telegram must have this number
+ CThis=((SwapWord(ResData->DataLen) - 4 ) / 4) + 1; // Partial counter
+ for (c=0; c < CThis+1; c++)
+ *List=SwapWord(ResData->Items[c].BlockNum);
+ Last++;
+ List++;
+ if (Last==0x8000)
+ Done=true;
+ Count+=CThis; // Total counter
+ List--;
+ Result=errCliItemNotAvailable;
+ //---------------------------------------------------------> Get next slice
+ while ((!Done) && (Result==0));
+ *Job.pAmount=0;
+ if (Count>Job.Amount)
+ Count=Job.Amount;
+ RoomError=true;
+ memcpy(Job.pData, &opData, Count*2);
+ *Job.pAmount=Count;
+ if (RoomError) // Result==0 -> override if romerror
+ Result=errCliPartialDataRead;
+void TSnap7MicroClient::FillTime(word SiemensTime, char *PTime)
+ // SiemensTime -> number of seconds after 1/1/1984
+ // This is not S7 date and time but is used only internally for block info
+ time_t TheDate = (SiemensTime * 86400)+ DeltaSecs;
+ struct tm * timeinfo = localtime (&TheDate);
+ if (timeinfo!=NULL) {
+ strftime(PTime,11,"%Y/%m/%d",timeinfo);
+ *PTime='\0';
+int TSnap7MicroClient::opAgBlockInfo()
+ PS7BlockInfo BlockInfo;
+ PReqDataBlockInfo ReqData;
+ PResDataBlockInfo ResData;
+ byte BlockType;
+ int BlockNum, IsoSize, Result;
+ BlockNum =Job.Number;
+ BlockInfo=PS7BlockInfo(Job.pData);
+ memset(BlockInfo,0,sizeof(TS7BlockInfo));
+ ReqData =PReqDataBlockInfo(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo));
+ ResData =PResDataBlockInfo(pbyte(ResParams)+sizeof(TResFunGetBlockInfo));
+ PDUH_out->DataLen=SwapWord(sizeof(TReqDataBlockInfo)); // 4 bytes data
+ ReqParams->SubFun =SFun_BlkInfo;
+ ReqData->DataLen =SwapWord(0x0008);
+ ReqData->BlkPrfx =0x30;
+ ReqData->A =0x41;
+ ReqData->AsciiBlk[0]=(BlockNum / 10000)+0x30;
+ BlockNum=BlockNum % 10000;
+ ReqData->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+ BlockNum=BlockNum % 1000;
+ ReqData->AsciiBlk[2]=(BlockNum / 100)+0x30;
+ BlockNum=BlockNum % 100;
+ ReqData->AsciiBlk[3]=(BlockNum / 10)+0x30;
+ BlockNum=BlockNum % 10;
+ ReqData->AsciiBlk[4]=(BlockNum / 1)+0x30;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunGetBlockInfo)+sizeof(TReqDataBlockInfo);
+ if (SwapWord(ResData->Length)<40) // 78
+ if (ResData->RetVal==0xFF) // <-- 0xFF means Result OK
+ //<----------------------------------------------Fill block info
+ BlockInfo->BlkType=ResData->SubBlkType;
+ BlockInfo->BlkNumber=SwapWord(ResData->BlkNumber);
+ BlockInfo->BlkLang=ResData->BlkLang;
+ BlockInfo->BlkFlags=ResData->BlkFlags;
+ BlockInfo->MC7Size=SwapWord(ResData->MC7Len);
+ BlockInfo->LoadSize=SwapDWord(ResData->LenLoadMem);
+ BlockInfo->LocalData=SwapWord(ResData->LocDataLen);
+ BlockInfo->SBBLength=SwapWord(ResData->SbbLen);
+ BlockInfo->CheckSum=SwapWord(ResData->BlkChksum);
+ BlockInfo->Version=ResData->Version;
+ memcpy(BlockInfo->Author, ResData->Author, 8);
+ memcpy(BlockInfo->Family,ResData->Family,8);
+ memcpy(BlockInfo->Header,ResData->Header,8);
+ FillTime(SwapWord(ResData->CodeTime_dy),BlockInfo->CodeDate);
+ FillTime(SwapWord(ResData->IntfTime_dy),BlockInfo->IntfDate);
+ //---------------------------------------------->Fill block info
+ Result=CpuError(ResData->RetVal);
+int TSnap7MicroClient::opDBGet()
+ TS7BlockInfo BI;
+ void * usrPData;
+ int * usrPSize;
+ int Result, Room;
+ // Stores user pointer
+ usrPData=Job.pData;
+ usrPSize=Job.pAmount;
+ Room =Job.Amount;
+ // 1 Pass : Get block info
+ Job.Area=Block_DB;
+ Job.pData=&BI;
+ Result=opAgBlockInfo();
+ // 2 Pass : Read the whole (MC7Size bytes) DB.
+ // Check user space
+ if (BI.MC7Size>Room)
+ Job.Amount=Room;
+ Job.Amount =BI.MC7Size;
+ // The data is read even if the buffer is small (the error is reported).
+ // Imagine that we want to read only a small amount of data at the
+ // beginning of a DB regardless it's size....
+ Job.Area =S7AreaDB;
+ Job.WordLen=S7WLByte;
+ Job.Start =0;
+ Job.pData =usrPData;
+ Result =opReadArea();
+ *usrPSize=Job.Amount;
+ if ((Result==0) && RoomError)
+ return errCliBufferTooSmall;
+int TSnap7MicroClient::opDBFill()
+ // new op : get block info
+ Job.Op =s7opAgBlockInfo;
+ Job.Area =Block_DB;
+ Result =opAgBlockInfo();
+ // Restore original op
+ // Fill internal buffer then write it
+ memset(&opData, byte(Job.IParam), Job.Amount);
+ Result =opWriteArea();
+int TSnap7MicroClient::opUpload()
+ byte Upload_ID = 0; // not strictly needed, only to avoid warning
+ int BlockNum, BlockLength, Result;
+ bool Done, Full; // if full==true, the data will be compatible to full download function
+ Full =Job.IParam==1;
+ // Setup Answer (is the same for all Upload pdus)
+ Answer= PS7ResHeader23(&PDU.Payload);
+ // Init sequence
+ Done =false;
+ //<-------------------------------------------------------------StartUpload
+ PReqFunStartUploadParams ReqParams;
+ PResFunStartUploadParams ResParams;
+ ReqParams=PReqFunStartUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=PResFunStartUploadParams(pbyte(Answer)+ResHeaderSize23);
+ // Init Header
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunStartUploadParams));// params size
+ PDUH_out->DataLen=0x0000; // No data
+ // Init Params
+ ReqParams->FunSUpld=pduStartUpload;
+ ReqParams->Uk6[0]=0x00;
+ ReqParams->Uk6[1]=0x00;
+ ReqParams->Uk6[2]=0x00;
+ ReqParams->Uk6[3]=0x00;
+ ReqParams->Uk6[4]=0x00;
+ ReqParams->Uk6[5]=0x00;
+ ReqParams->Upload_ID=Upload_ID; // At begining is 0) we will put upload id incoming from plc
+ ReqParams->Len_1 =0x09; // 9 bytes from here
+ ReqParams->Prefix=0x5F;
+ ReqParams->BlkPrfx=0x30; // '0'
+ ReqParams->BlkType=BlockType;
+ // Block number
+ ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30;
+ ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+ ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30;
+ ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30;
+ ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30;
+ ReqParams->A=0x41; // 'A'
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunStartUploadParams);
+ // Get Upload Infos (only ID now)
+ if (Answer->Error==0)
+ Upload_ID=ResParams->Upload_ID;
+ Result=CpuError(SwapWord(Answer->Error));
+ //------------------------------------------------------------->StartUpload
+ //<--------------------------------------------------------FirstUpload
+ PReqFunUploadParams ReqParams;
+ PResFunUploadParams ResParams;
+ PResFunUploadDataHeaderFirst ResDataHeader;
+ ReqParams=PReqFunUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ // First upload pdu consists of params, block info header, data.
+ ResParams=PResFunUploadParams(pbyte(Answer)+ResHeaderSize23);
+ ResDataHeader=PResFunUploadDataHeaderFirst(pbyte(ResParams)+sizeof(TResFunUploadParams));
+ if (Full)
+ Source=pbyte(ResDataHeader)+4; // skip only the mini header
+ Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderFirst); // not full : skip the data header
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunUploadParams));// params size
+ ReqParams->FunUpld=pduUpload;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunUploadParams);
+ Done=ResParams->EoU==0;
+ Size=SwapWord(Answer->DataLen)-4; // Full data Size
+ Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderFirst); // Size of this data slice
+ BlockLength=SwapWord(ResDataHeader->MC7Len); // Full block size in byte
+ Target=pbyte(&opData)+Offset;
+ Result=errCliUploadSequenceFailed;
+ //-------------------------------------------------------->FirstUpload
+ while (!Done && (Result==0))
+ //<----------------------------------------------------NextUpload
+ PResFunUploadDataHeaderNext ResDataHeader;
+ // Next upload pdu consists of params, small info header, data.
+ ResDataHeader=PResFunUploadDataHeaderNext(pbyte(ResParams)+sizeof(TResFunUploadParams));
+ Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderNext);
+ Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderNext); // Size of this data slice
+ //---------------------------------------------------->NextUpload
+ //<----------------------------------------------------EndUpload;
+ PReqFunEndUploadParams ReqParams;
+ PResFunEndUploadParams ResParams;
+ ReqParams=PReqFunEndUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=PResFunEndUploadParams(pbyte(Answer)+ResHeaderSize23);
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunEndUploadParams));// params size
+ ReqParams->FunEUpld=pduEndUpload;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunEndUploadParams);
+ // Get EndUpload Result
+ if ((Answer->Error!=0) || (ResParams->FunEUpld!=pduEndUpload))
+ //---------------------------------------------------->EndUpload;
+ opSize=int(Offset);
+ if (opSize<78)
+ Result=errCliInvalidDataSizeRecvd;
+ opSize=BlockLength;
+ if (opSize<1)
+ // Checks user space
+ if (Job.Amount<opSize) {
+ opSize=Job.Amount;
+ RoomError = true;
+ memcpy(Job.pData, &opData, opSize);
+ *Job.pAmount=opSize;
+int TSnap7MicroClient::opDownload()
+ PS7CompactBlockInfo Info;
+ PS7BlockFooter Footer;
+ int BlockNum, StoreBlockNum, BlockAmount;
+ int BlockSize, BlockSizeLd;
+ int BlockType, Remainder;
+ int Result, IsoSize;
+ BlockAmount=Job.Amount;
+ Result=CheckBlock(-1,-1,&opData,BlockAmount);
+ Info=PS7CompactBlockInfo(&opData);
+ // Gets blocktype
+ BlockType=SubBlockToBlock(Info->SubBlkType);
+ if (BlockNum>=0)
+ Info->BlkNum=SwapWord(BlockNum); // change the number
+ BlockNum=SwapWord(Info->BlkNum); // use the header's number
+ BlockSizeLd=BlockAmount; // load mem needed for this block
+ BlockSize =SwapWord(Info->MC7Len); // net size
+ Footer=PS7BlockFooter(pbyte(&opData)+BlockSizeLd-sizeof(TS7BlockFooter));
+ Footer->Chksum=0x0000;
+ Remainder=BlockAmount;
+ //<---------------------------------------------- Start Download request
+ PReqStartDownloadParams ReqParams;
+ PResStartDownloadParams ResParams;
+ ReqParams=PReqStartDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=PResStartDownloadParams(pbyte(Answer)+ResHeaderSize23);
+ PDUH_out->ParLen=SwapWord(sizeof(TReqStartDownloadParams));
+ ReqParams->FunSDwnld = pduReqDownload;
+ ReqParams->Uk6[1]=0x01;
+ ReqParams->Dwnld_ID=0x00;
+ ReqParams->Len_1 =0x09;
+ ReqParams->BlkPrfx=0x30;
+ StoreBlockNum=BlockNum;
+ ReqParams->P =0x50;
+ ReqParams->Len_2=0x0D;
+ ReqParams->Uk1 =0x31; // '1'
+ BlockNum=StoreBlockNum;
+ // Load memory
+ ReqParams->AsciiLoad[0]=(BlockSizeLd / 100000)+0x30;
+ BlockSizeLd=BlockSizeLd % 100000;
+ ReqParams->AsciiLoad[1]=(BlockSizeLd / 10000)+0x30;
+ BlockSizeLd=BlockSizeLd % 10000;
+ ReqParams->AsciiLoad[2]=(BlockSizeLd / 1000)+0x30;
+ BlockSizeLd=BlockSizeLd % 1000;
+ ReqParams->AsciiLoad[3]=(BlockSizeLd / 100)+0x30;
+ BlockSizeLd=BlockSizeLd % 100;
+ ReqParams->AsciiLoad[4]=(BlockSizeLd / 10)+0x30;
+ BlockSizeLd=BlockSizeLd % 10;
+ ReqParams->AsciiLoad[5]=(BlockSizeLd / 1)+0x30;
+ // MC7 memory
+ ReqParams->AsciiMC7[0]=(BlockSize / 100000)+0x30;
+ BlockSize=BlockSize % 100000;
+ ReqParams->AsciiMC7[1]=(BlockSize / 10000)+0x30;
+ BlockSize=BlockSize % 10000;
+ ReqParams->AsciiMC7[2]=(BlockSize / 1000)+0x30;
+ BlockSize=BlockSize % 1000;
+ ReqParams->AsciiMC7[3]=(BlockSize / 100)+0x30;
+ BlockSize=BlockSize % 100;
+ ReqParams->AsciiMC7[4]=(BlockSize / 10)+0x30;
+ BlockSize=BlockSize % 10;
+ ReqParams->AsciiMC7[5]=(BlockSize / 1)+0x30;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqStartDownloadParams);
+ // Get Result
+ if (SwapWord(Answer->Error)!=Code7NeedPassword)
+ if ((Answer->Error!=0) || (*ResParams!=pduReqDownload))
+ Result=errCliDownloadSequenceFailed;
+ Result=errCliNeedPassword;
+ //----------------------------------------------> Start Download request
+ //<-------------------------------- Download sequence (PLC requests)
+ PReqDownloadParams ReqParams;
+ PResDownloadParams ResParams;
+ PResDownloadDataHeader ResData;
+ int Slice, Size, MaxSlice;
+ word Sequence;
+ ReqParams=PReqDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=PResDownloadParams(pbyte(Answer)+ResHeaderSize23);
+ ResData =PResDownloadDataHeader(pbyte(ResParams)+sizeof(TResDownloadParams));
+ Target =pbyte(ResData)+sizeof(TResDownloadDataHeader);
+ Source =pbyte(&opData)+Offset;
+ Result=isoRecvBuffer(0,Size);
+ if ((u_int(Size)>sizeof(TS7ReqHeader)) && (ReqParams->Fun==pduDownload))
+ Sequence=PDUH_out->Sequence;
+ // Max data slice that we can fit in this pdu
+ MaxSlice=PDULength-ResHeaderSize23-sizeof(TResDownloadParams)-sizeof(TResDownloadDataHeader);
+ Slice=Remainder;
+ if (Slice>MaxSlice)
+ Slice=MaxSlice;
+ Remainder-=Slice;
+ Offset+=Slice;
+ Done=Remainder<=0;
+ // Init Answer
+ Answer->P=0x32;
+ Answer->PDUType=PduType_response;
+ Answer->AB_EX=0x0000;
+ Answer->Sequence=Sequence;
+ Answer->ParLen =SwapWord(sizeof(TResDownloadParams));
+ Answer->DataLen=SwapWord(word(sizeof(TResDownloadDataHeader))+Slice);
+ Answer->Error =0x0000;
+ ResParams->FunDwnld=pduDownload;
+ if (Remainder>0)
+ ResParams->EoS=0x01;
+ ResParams->EoS=0x00;
+ // Init Data
+ ResData->DataLen=SwapWord(Slice);
+ ResData->FB_00=0xFB00;
+ memcpy(Target, Source, Slice);
+ // Send the slice
+ IsoSize=ResHeaderSize23+sizeof(TResDownloadParams)+sizeof(TResDownloadDataHeader)+Slice;
+ Result=isoSendBuffer(0,IsoSize);
+ //--------------------------------> Download sequence (PLC requests)
+ while (!Done && (Result==0));
+ //<-------------------------------------------Perform Download Ended
+ PResEndDownloadParams ResParams;
+ ResParams=PResEndDownloadParams(pbyte(Answer)+ResHeaderSize23);
+ if ((u_int(Size)>sizeof(TS7ReqHeader)) && (ReqParams->Fun==pduDownloadEnded))
+ Answer->ParLen =SwapWord(sizeof(TResEndDownloadParams));
+ Answer->DataLen=0x0000;
+ *ResParams=pduDownloadEnded;
+ IsoSize=ResHeaderSize23+sizeof(TResEndDownloadParams);
+ //------------------------------------------->Perform Download Ended
+ //<----------------------------------- Insert block into the unit
+ PReqControlBlockParams ReqParams;
+ pbyte ResParams;
+ ReqParams=PReqControlBlockParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ Answer=PS7ResHeader23(&PDU.Payload);
+ ResParams=pbyte(Answer)+ResHeaderSize23;
+ PDUH_out->ParLen=SwapWord(sizeof(TReqControlBlockParams));
+ ReqParams->Fun = pduControl;
+ ReqParams->Uk7[0]=0x00;
+ ReqParams->Uk7[1]=0x00;
+ ReqParams->Uk7[2]=0x00;
+ ReqParams->Uk7[3]=0x00;
+ ReqParams->Uk7[4]=0x00;
+ ReqParams->Uk7[5]=0x00;
+ ReqParams->Uk7[6]=0xFD;
+ ReqParams->Len_1 =SwapWord(0x0A);
+ ReqParams->NumOfBlocks=0x01;
+ ReqParams->ByteZero =0x00;
+ ReqParams->AsciiZero =0x30;
+ ReqParams->SFun =SFun_Insert;
+ ReqParams->Len_2=0x05;
+ ReqParams->Cmd[0]='_';
+ ReqParams->Cmd[1]='I';
+ ReqParams->Cmd[2]='N';
+ ReqParams->Cmd[3]='S';
+ ReqParams->Cmd[4]='E';
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqControlBlockParams);
+ if ((Answer->Error!=0) || (*ResParams!=pduControl))
+ Result=errCliInsertRefused;
+ //-----------------------------------> Insert block into the unit
+int TSnap7MicroClient::opDelete()
+ int IsoSize, BlockType, BlockNum, Result;
+ ReqParams->SFun =SFun_Delete;
+ ReqParams->Cmd[1]='D';
+ ReqParams->Cmd[2]='E';
+ ReqParams->Cmd[3]='L';
+ Result=errCliDeleteRefused;
+int TSnap7MicroClient::opReadSZL()
+ PS7Answer17 Answer;
+ PReqFunReadSZLFirst ReqParamsFirst;
+ PReqFunReadSZLNext ReqParamsNext;
+ PS7ReqSZLData ReqDataFirst;
+ PS7ReqSZLData ReqDataNext;
+ PS7ResParams7 ResParams;
+ PS7ResSZLDataFirst ResDataFirst;
+ PS7ResSZLDataNext ResDataNext;
+ PSZL_HEADER Header;
+ PS7SZLList Target;
+ pbyte PDataFirst;
+ pbyte PDataNext;
+ word ID, Index;
+ int IsoSize, DataSize, DataSZL, Result;
+ bool First, Done;
+ bool NoRoom = false;
+ uintptr_t Offset =0;
+ byte Seq_in =0x00;
+ ID=Job.ID;
+ Index=Job.Index;
+ opSize=0;
+ ReqParamsFirst=PReqFunReadSZLFirst(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqParamsNext =PReqFunReadSZLNext(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqDataFirst =PS7ReqSZLData(pbyte(ReqParamsFirst)+sizeof(TReqFunReadSZLFirst));
+ ReqDataNext =PS7ReqSZLData(pbyte(ReqParamsNext)+sizeof(TReqFunReadSZLNext));
+ Answer =PS7Answer17(&PDU.Payload);
+ ResParams =PS7ResParams7(pbyte(Answer)+ResHeaderSize17);
+ ResDataFirst =PS7ResSZLDataFirst(pbyte(ResParams)+sizeof(TS7Params7));
+ ResDataNext =PS7ResSZLDataNext(pbyte(ResParams)+sizeof(TS7Params7));
+ PDataFirst =pbyte(ResDataFirst)+8; // skip header
+ PDataNext =pbyte(ResDataNext)+4; // skip header
+ Header =PSZL_HEADER(&opData);
+ First=true;
+ //<------------------------------------------------------- read slices
+ //<-------------------------------------------------- prepare first
+ DataSize=sizeof(TS7ReqSZLData);
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLFirst)); // 8 bytes params
+ PDUH_out->DataLen=SwapWord(DataSize); // 8/4 bytes data
+ ReqParamsFirst->Head[0]=0x00;
+ ReqParamsFirst->Head[1]=0x01;
+ ReqParamsFirst->Head[2]=0x12;
+ ReqParamsFirst->Plen =0x04;
+ ReqParamsFirst->Uk =0x11;
+ ReqParamsFirst->Tg =grSZL;
+ ReqParamsFirst->SubFun =SFun_ReadSZL; //0x03
+ ReqParamsFirst->Seq =Seq_in;
+ // Fill Data
+ ReqDataFirst->Ret =0xFF;
+ ReqDataFirst->TS =TS_ResOctet;
+ ReqDataFirst->DLen =SwapWord(0x0004);
+ ReqDataFirst->ID =SwapWord(ID);
+ ReqDataFirst->Index =SwapWord(Index);
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunReadSZLFirst)+DataSize;
+ //--------------------------------------------------> prepare first
+ //<-------------------------------------------------- prepare next
+ DataSize=sizeof(TS7ReqSZLData)-4;
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLNext)); // 8 bytes params
+ PDUH_out->DataLen=SwapWord(DataSize);// 8/4 bytes data
+ ReqParamsNext->Head[0]=0x00;
+ ReqParamsNext->Head[1]=0x01;
+ ReqParamsNext->Head[2]=0x12;
+ ReqParamsNext->Plen =0x08;
+ ReqParamsNext->Uk =0x12;
+ ReqParamsNext->Tg =grSZL;
+ ReqParamsNext->SubFun =SFun_ReadSZL;
+ ReqParamsNext->Seq =Seq_in;
+ ReqParamsNext->Rsvd =0x0000;
+ ReqParamsNext->ErrNo =0x0000;
+ ReqDataNext->Ret =0x0A;
+ ReqDataNext->TS =0x00;
+ ReqDataNext->DLen =0x0000;
+ ReqDataNext->ID =0x0000;
+ ReqDataNext->Index =0x0000;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunReadSZLNext)+DataSize;
+ //--------------------------------------------------> prepare next
+ //<------------------------------------------ get data first
+ if (ResParams->Err==0)
+ if (ResDataFirst->Ret==0xFF) // <-- 0xFF means Result OK
+ // Gets Amount of this slice
+ DataSZL=SwapWord(ResDataFirst->DLen)-4;// Skips extra params (ID, Index ...)
+ // Gets end of Sequence Flag
+ Done=(ResParams->resvd & 0xFF00) == 0; // Low order byte = 0x00 => the sequence is done
+ // Gets Unit's function sequence
+ Seq_in=ResParams->Seq;
+ Target=PS7SZLList(pbyte(&opData)+Offset);
+ memcpy(Target, PDataFirst, DataSZL);
+ Offset+=DataSZL;
+ Result=CpuError(ResDataFirst->Ret);
+ //------------------------------------------> get data first
+ //<------------------------------------------ get data next
+ if (ResDataNext->Ret==0xFF) // <-- 0xFF means Result OK
+ DataSZL=SwapWord(ResDataNext->DLen);
+ memcpy(Target, PDataNext, DataSZL);
+ Result=CpuError(ResDataNext->Ret);
+ //------------------------------------------> get data next
+ //-------------------------------------------------------> read slices
+ // Check errors and adjust header
+ // Adjust big endian header
+ Header->LENTHDR=SwapWord(Header->LENTHDR);
+ Header->N_DR =SwapWord(Header->N_DR);
+ if (Job.IParam==1) // if 1 data has to be copied into user buffer
+ // Check buffer size
+ if (opSize>Job.Amount)
+ NoRoom=true;
+ if ((Result==0)&& NoRoom)
+ Result=errCliBufferTooSmall;
+int TSnap7MicroClient::opReadSZLList()
+ PS7SZLList usrSZLList, opDataList;
+ int ItemsCount, ItemsCount_in, c, Result;
+ Job.ID =0x0000;
+ Job.Index =0x0000;
+ Job.IParam =0;
+ ItemsCount_in=Job.Amount; // stores the room
+ Job.Amount =sizeof(opData); // read into the internal buffer
+ Result =opReadSZL();
+ opDataList=PS7SZLList(&opData); // Source
+ usrSZLList=PS7SZLList(Job.pData); // Target
+ ItemsCount=(opSize-sizeof(SZL_HEADER)) / 2;
+ // Check input size
+ if (ItemsCount>ItemsCount_in)
+ ItemsCount=ItemsCount_in; // Trim itemscount
+ usrSZLList->List[c]=SwapWord(opDataList->List[c]);
+ *Job.pAmount=ItemsCount;
+ if ((Result==0) && NoRoom)
+byte TSnap7MicroClient::BCDtoByte(byte B)
+ return ((B >> 4) * 10) + (B & 0x0F);
+byte TSnap7MicroClient::WordToBCD(word Value)
+ return ((Value / 10) << 4) | (Value % 10);
+int TSnap7MicroClient::opGetDateTime()
+ PTimeStruct DateTime;
+ PReqFunDateTime ReqParams;
+ PReqDataGetDateTime ReqData;
+ PResDataGetTime ResData;
+ word AYear;
+ ReqParams=PReqFunDateTime(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqData =PReqDataGetDateTime(pbyte(ReqParams)+sizeof(TReqFunDateTime));
+ ResParams=PS7ResParams7(pbyte(Answer)+ResHeaderSize17);
+ ResData =PResDataGetTime(pbyte(ResParams)+sizeof(TS7Params7));
+ DateTime =PTimeStruct(Job.pData);
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunDateTime)); // 8 bytes params
+ PDUH_out->DataLen=SwapWord(sizeof(TReqDataGetDateTime)); // 4 bytes data
+ ReqParams->Tg =grClock;
+ ReqParams->SubFun =SFun_ReadClock;
+ *ReqData =0x0000000A;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunDateTime)+sizeof(TReqDataGetDateTime);
+ // Decode Plc Date and Time
+ AYear=BCDtoByte(ResData->Time[0]);
+ if (AYear<90)
+ AYear=AYear+100;
+ DateTime->tm_year=AYear;
+ DateTime->tm_mon =BCDtoByte(ResData->Time[1])-1;
+ DateTime->tm_mday=BCDtoByte(ResData->Time[2]);
+ DateTime->tm_hour=BCDtoByte(ResData->Time[3]);
+ DateTime->tm_min =BCDtoByte(ResData->Time[4]);
+ DateTime->tm_sec =BCDtoByte(ResData->Time[5]);
+ DateTime->tm_wday=(ResData->Time[7] & 0x0F)-1;
+int TSnap7MicroClient::opSetDateTime()
+ PReqDataSetTime ReqData;
+ ReqData =PReqDataSetTime(pbyte(ReqParams)+sizeof(TReqFunDateTime));
+ PDUH_out->DataLen=SwapWord(sizeof(TReqDataSetTime)); // 4 bytes data
+ ReqParams->SubFun =SFun_SetClock;
+ // EncodeSiemensDateTime;
+ if (DateTime->tm_year<100)
+ AYear=DateTime->tm_year;
+ AYear=DateTime->tm_year-100;
+ ReqData->RetVal=0xFF;
+ ReqData->Length=SwapWord(0x000A);
+ ReqData->Rsvd =0x00;
+ ReqData->HiYear=0x19; // *must* be 19 tough it's not the Hi part of the year...
+ ReqData->Time[0]=WordToBCD(AYear);
+ ReqData->Time[1]=WordToBCD(DateTime->tm_mon+1);
+ ReqData->Time[2]=WordToBCD(DateTime->tm_mday);
+ ReqData->Time[3]=WordToBCD(DateTime->tm_hour);
+ ReqData->Time[4]=WordToBCD(DateTime->tm_min);
+ ReqData->Time[5]=WordToBCD(DateTime->tm_sec);
+ ReqData->Time[6]=0;
+ ReqData->Time[7]=DateTime->tm_wday+1;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunDateTime)+sizeof(TReqDataSetTime);
+ if (ResParams->Err!=0)
+ Result=CpuError(SwapWord(ResParams->Err));
+int TSnap7MicroClient::opGetOrderCode()
+ PS7OrderCode OC;
+ Job.ID =0x0011;
+ OC=PS7OrderCode(Job.pData);
+ memset(OC,0,sizeof(TS7OrderCode));
+ memcpy(OC->Code,&opData[6],20);
+ OC->V1=opData[opSize-3];
+ OC->V2=opData[opSize-2];
+ OC->V3=opData[opSize-1];
+int TSnap7MicroClient::opGetCpuInfo()
+ PS7CpuInfo Info;
+ // Store Pointer
+ Info=PS7CpuInfo(Job.pData);
+ // Clear data in order to have the end of strings (\0) correctly setted
+ memset(Info, 0, sizeof(TS7CpuInfo));
+ Job.ID =0x001C;
+ Job.IParam=0;
+ memcpy(Info->ModuleTypeName,&opData[176],32);
+ memcpy(Info->SerialNumber,&opData[142],24);
+ memcpy(Info->ASName,&opData[6],24);
+ memcpy(Info->Copyright,&opData[108],26);
+ memcpy(Info->ModuleName,&opData[40],24);
+int TSnap7MicroClient::opGetCpInfo()
+ PS7CpInfo Info;
+ Info=PS7CpInfo(Job.pData);
+ memset(Info,0,sizeof(TS7CpInfo));
+ Job.ID =0x0131;
+ Job.Index =0x0001;
+ Info->MaxPduLengt=opData[6]*256+opData[7];
+ Info->MaxConnections=opData[8]*256+opData[9];
+ Info->MaxMpiRate=DWordAt(&opData[10]);
+ Info->MaxBusRate=DWordAt(&opData[14]);
+int TSnap7MicroClient::opGetPlcStatus()
+ int *Status;
+ Status =(int*)Job.pData;
+ Job.ID =0x0424;
+ switch (opData[7])
+ case S7CpuStatusUnknown :
+ case S7CpuStatusRun :
+ case S7CpuStatusStop : *Status=opData[7];
+ // Since RUN status is always $08 for all CPUs and CPs, STOP status
+ // sometime can be coded as $03 (especially for old cpu...)
+ *Status=S7CpuStatusStop;
+ *Status=0;
+int TSnap7MicroClient::opPlcStop()
+ PReqFunPlcStop ReqParams;
+ PResFunCtrl ResParams;
+ char p_program[] = {'P','_','P','R','O','G','R','A','M'};
+ ReqParams=PReqFunPlcStop(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcStop));
+ PDUH_out->DataLen=0x0000; // No Data
+ ReqParams->Fun=pduStop;
+ memset(ReqParams->Uk_5,0,5);
+ ReqParams->Len_2=0x09;
+ memcpy(ReqParams->Cmd,&p_program,9);
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcStop);
+ if (ResParams->ResFun!=pduStop)
+ Result=errCliCannotStopPLC;
+ if (ResParams->para ==0x07)
+ Result=errCliAlreadyStop;
+int TSnap7MicroClient::opPlcHotStart()
+ PReqFunPlcHotStart ReqParams;
+ ReqParams=PReqFunPlcHotStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcHotStart)); // 16 bytes params
+ ReqParams->Fun=pduStart;
+ ReqParams->Uk_7[0]=0x00;
+ ReqParams->Uk_7[1]=0x00;
+ ReqParams->Uk_7[2]=0x00;
+ ReqParams->Uk_7[3]=0x00;
+ ReqParams->Uk_7[4]=0x00;
+ ReqParams->Uk_7[5]=0x00;
+ ReqParams->Uk_7[6]=0xFD;
+ ReqParams->Len_1=0x0000;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcHotStart);
+ if ((Answer->Error!=0))
+ if ((ResParams->ResFun!=pduStart))
+ Result=errCliCannotStartPLC;
+ if (ResParams->para==0x03)
+ Result=errCliAlreadyRun;
+ if (ResParams->para==0x02)
+int TSnap7MicroClient::opPlcColdStart()
+ PReqFunPlcColdStart ReqParams;
+ ReqParams=PReqFunPlcColdStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunPlcColdStart)); // 22 bytes params
+ ReqParams->Len_1=SwapWord(0x0002);
+ ReqParams->SFun =SwapWord(0x4320); // Cold start
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcColdStart);
+int TSnap7MicroClient::opCopyRamToRom()
+ PReqFunCopyRamToRom ReqParams;
+ int IsoSize, CurTimeout, Result;
+ char _modu[] = {'_','M','O','D','U'};
+ ReqParams=PReqFunCopyRamToRom(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunCopyRamToRom));
+ ReqParams->Fun=pduControl;
+ ReqParams->SFun =SwapWord(0x4550);
+ memcpy(ReqParams->Cmd,&_modu,5);
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCopyRamToRom);
+ // Changes the timeout
+ CurTimeout=RecvTimeout;
+ RecvTimeout=Job.IParam;
+ // Restores the timeout
+ RecvTimeout=CurTimeout;
+ if ((Answer->Error!=0) || (ResParams->ResFun!=pduControl))
+ Result=errCliCannotCopyRamToRom;
+int TSnap7MicroClient::opCompress()
+ PReqFunCompress ReqParams;
+ char _garb[] = {'_','G','A','R','B'};
+ ReqParams=PReqFunCompress(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ PDUH_out->ParLen=SwapWord(sizeof(TReqFunCompress));
+ memcpy(ReqParams->Cmd,&_garb,5);
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCompress);
+ if (((Answer->Error!=0) || (ResParams->ResFun!=pduControl)))
+ Result=errCliCannotCompress;
+int TSnap7MicroClient::opGetProtection()
+ PS7Protection Info, usrInfo;
+ usrInfo=PS7Protection(Job.pData);
+ memset(usrInfo, 0, sizeof(TS7Protection));
+ Job.ID =0x0232;
+ Job.Index =0x0004;
+ Job.IParam=0; // No copy in Usr Data pointed by Job.pData
+ Info=PS7Protection(pbyte(&opData)+6);
+ usrInfo->sch_schal=SwapWord(Info->sch_schal);
+ usrInfo->sch_par =SwapWord(Info->sch_par);
+ usrInfo->sch_rel =SwapWord(Info->sch_rel);
+ usrInfo->bart_sch =SwapWord(Info->bart_sch);
+ usrInfo->anl_sch =SwapWord(Info->anl_sch);
+// NOTE
+// PASSWORD HACKING IS VERY FAR FROM THE AIM OF THIS PROJECT
+// NEXT FUNCTION ONLY ENCODES THE ASCII PASSWORD TO BE DOWNLOADED IN THE PLC.
+// MOREOVER **YOU NEED TO KNOW** THE CORRECT PASSWORD TO MEET THE CPU
+// SECURITY LEVEL
+int TSnap7MicroClient::opSetPassword()
+ PReqFunSecurity ReqParams;
+ PReqDataSecurity ReqData;
+ PResParamsSecurity ResParams;
+ int c, IsoSize, Result;
+ ReqParams=PReqFunSecurity(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ReqData =PReqDataSecurity(pbyte(ReqParams)+sizeof(TReqFunSecurity));
+ ResParams=PResParamsSecurity(pbyte(Answer)+ResHeaderSize17);
+ PDUH_out->ParLen =SwapWord(sizeof(TReqFunSecurity));
+ PDUH_out->DataLen=SwapWord(sizeof(TReqDataSecurity));
+ ReqParams->Tg =grSecurity;
+ ReqParams->SubFun =SFun_EnterPwd;
+ ReqData->Ret =0xFF;
+ ReqData->TS =TS_ResOctet;
+ ReqData->DLen =SwapWord(0x0008); // 8 bytes data : password
+ // Encode the password
+ ReqData->Pwd[0]=opData[0] ^ 0x55;
+ ReqData->Pwd[1]=opData[1] ^ 0x55;
+ for (c = 2; c < 8; c++){
+ ReqData->Pwd[c]=opData[c] ^ 0x55 ^ ReqData->Pwd[c-2];
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunSecurity)+sizeof(TReqDataSecurity);
+ // Get Return
+int TSnap7MicroClient::opClearPassword()
+ PDUH_out->DataLen=SwapWord(0x0004); // We need only 4 bytes
+ ReqParams->SubFun =SFun_CancelPwd;
+ ReqData->Ret =0x0A;
+ ReqData->TS =0x00;
+ ReqData->DLen =0x0000;
+ IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunSecurity)+4;
+int TSnap7MicroClient::CpuError(int Error)
+ switch(Error)
+ case 0 : return 0;
+ case Code7AddressOutOfRange : return errCliAddressOutOfRange;
+ case Code7InvalidTransportSize : return errCliInvalidTransportSize;
+ case Code7WriteDataSizeMismatch : return errCliWriteDataSizeMismatch;
+ case Code7ResItemNotAvailable :
+ case Code7ResItemNotAvailable1 : return errCliItemNotAvailable;
+ case Code7DataOverPDU : return errCliSizeOverPDU;
+ case Code7InvalidValue : return errCliInvalidValue;
+ case Code7FunNotAvailable : return errCliFunNotAvailable;
+ case Code7NeedPassword : return errCliNeedPassword;
+ case Code7InvalidPassword : return errCliInvalidPassword;
+ case Code7NoPasswordToSet :
+ case Code7NoPasswordToClear : return errCliNoPasswordToSetOrClear;
+ return errCliFunctionRefused;
+int TSnap7MicroClient::DataSizeByte(int WordLength)
+ switch (WordLength){
+ case S7WLBit : return 1; // S7 sends 1 byte per bit
+ case S7WLByte : return 1;
+ case S7WLChar : return 1;
+ case S7WLWord : return 2;
+ case S7WLDWord : return 4;
+ case S7WLInt : return 2;
+ case S7WLDInt : return 4;
+ case S7WLReal : return 4;
+ case S7WLCounter : return 2;
+ case S7WLTimer : return 2;
+ default : return 0;
+longword TSnap7MicroClient::DWordAt(void * P)
+ longword DW;
+ DW=*(longword*)P;
+ return SwapDWord(DW);
+int TSnap7MicroClient::CheckBlock(int BlockType, int BlockNum, void * pBlock, int Size)
+ PS7CompactBlockInfo Info = PS7CompactBlockInfo(pBlock);
+ if (BlockType>=0) // if (BlockType<0 the test is skipped
+ if ((BlockType!=Block_OB)&&(BlockType!=Block_DB)&&(BlockType!=Block_FB)&&
+ (BlockType!=Block_FC)&&(BlockType!=Block_SDB)&&(BlockType!=Block_SFC)&&
+ (BlockType!=Block_SFB))
+ return errCliInvalidBlockType;
+ if (BlockNum>=0) // if (BlockNum<0 the test is skipped
+ if (BlockNum>0xFFFF)
+ return errCliInvalidBlockNumber;
+ if (SwapDWord(Info->LenLoadMem)!=longword(Size))
+ return errCliInvalidBlockSize;
+ // Check the presence of the footer
+ if (SwapWord(Info->MC7Len)+sizeof(TS7CompactBlockInfo)>=u_int(Size))
+int TSnap7MicroClient::SubBlockToBlock(int SBB)
+ switch (SBB)
+ case SubBlk_OB : return Block_OB;
+ case SubBlk_DB : return Block_DB;
+ case SubBlk_SDB : return Block_SDB;
+ case SubBlk_FC : return Block_FC;
+ case SubBlk_SFC : return Block_SFC;
+ case SubBlk_FB : return Block_FB;
+ case SubBlk_SFB : return Block_SFB;
+int TSnap7MicroClient::PerformOperation()
+ int Operation=Job.Op;
+ switch(Operation)
+ case s7opNone:
+ Job.Result=errCliInvalidParams;
+ case s7opReadArea:
+ Job.Result=opReadArea();
+ case s7opWriteArea:
+ Job.Result=opWriteArea();
+ case s7opReadMultiVars:
+ Job.Result=opReadMultiVars();
+ case s7opWriteMultiVars:
+ Job.Result=opWriteMultiVars();
+ case s7opDBGet:
+ Job.Result=opDBGet();
+ case s7opDBFill:
+ Job.Result=opDBFill();
+ case s7opUpload:
+ Job.Result=opUpload();
+ case s7opDownload:
+ Job.Result=opDownload();
+ case s7opDelete:
+ Job.Result=opDelete();
+ case s7opListBlocks:
+ Job.Result=opListBlocks();
+ case s7opAgBlockInfo:
+ Job.Result=opAgBlockInfo();
+ case s7opListBlocksOfType:
+ Job.Result=opListBlocksOfType();
+ case s7opReadSzlList:
+ Job.Result=opReadSZLList();
+ case s7opReadSZL:
+ Job.Result=opReadSZL();
+ case s7opGetDateTime:
+ Job.Result=opGetDateTime();
+ case s7opSetDateTime:
+ Job.Result=opSetDateTime();
+ case s7opGetOrderCode:
+ Job.Result=opGetOrderCode();
+ case s7opGetCpuInfo:
+ Job.Result=opGetCpuInfo();
+ case s7opGetCpInfo:
+ Job.Result=opGetCpInfo();
+ case s7opGetPlcStatus:
+ Job.Result=opGetPlcStatus();
+ case s7opPlcHotStart:
+ Job.Result=opPlcHotStart();
+ case s7opPlcColdStart:
+ Job.Result=opPlcColdStart();
+ case s7opCopyRamToRom:
+ Job.Result=opCopyRamToRom();
+ case s7opCompress:
+ Job.Result=opCompress();
+ case s7opPlcStop:
+ Job.Result=opPlcStop();
+ case s7opGetProtection:
+ Job.Result=opGetProtection();
+ case s7opSetPassword:
+ Job.Result=opSetPassword();
+ case s7opClearPassword:
+ Job.Result=opClearPassword();
+ Job.Time =SysGetTick()-JobStart;
+ Job.Pending=false;
+ return SetError(Job.Result);
+int TSnap7MicroClient::Disconnect()
+ JobStart=SysGetTick();
+ PeerDisconnect();
+ Job.Time=SysGetTick()-JobStart;
+int TSnap7MicroClient::Reset(bool DoReconnect)
+ if (DoReconnect) {
+int TSnap7MicroClient::Connect()
+ Result =PeerConnect();
+void TSnap7MicroClient::SetConnectionType(word ConnType)
+ ConnectionType=ConnType;
+void TSnap7MicroClient::SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTSAP)
+ SrcTSap = LocalTSAP;
+ DstTSap = RemoteTSAP;
+ strncpy(RemoteAddress, RemAddress, 16);
+int TSnap7MicroClient::ConnectTo(const char *RemAddress, int Rack, int Slot)
+ word RemoteTSAP = (ConnectionType<<8)+(Rack*0x20)+Slot;
+ SetConnectionParams(RemAddress, SrcTSap, RemoteTSAP);
+int TSnap7MicroClient::GetParam(int ParamNumber, void *pValue)
+ switch (ParamNumber)
+ case p_u16_RemotePort:
+ *Puint16_t(pValue)=RemotePort;
+ case p_i32_PingTimeout:
+ *Pint32_t(pValue)=PingTimeout;
+ case p_i32_SendTimeout:
+ *Pint32_t(pValue)=SendTimeout;
+ case p_i32_RecvTimeout:
+ *Pint32_t(pValue)=RecvTimeout;
+ case p_i32_WorkInterval:
+ *Pint32_t(pValue)=WorkInterval;
+ case p_u16_SrcRef:
+ *Puint16_t(pValue)=SrcRef;
+ case p_u16_DstRef:
+ *Puint16_t(pValue)=DstRef;
+ case p_u16_SrcTSap:
+ *Puint16_t(pValue)=SrcTSap;
+ case p_i32_PDURequest:
+ *Pint32_t(pValue)=PDURequest;
+ default: return errCliInvalidParamNumber;
+int TSnap7MicroClient::SetParam(int ParamNumber, void *pValue)
+ if (!Connected)
+ RemotePort=*Puint16_t(pValue);
+ return errCliCannotChangeParam;
+ PingTimeout=*Pint32_t(pValue);
+ SendTimeout=*Pint32_t(pValue);
+ RecvTimeout=*Pint32_t(pValue);
+ WorkInterval=*Pint32_t(pValue);
+ SrcRef=*Puint16_t(pValue);
+ DstRef=*Puint16_t(pValue);
+ SrcTSap=*Puint16_t(pValue);
+ PDURequest=*Pint32_t(pValue);
+// Data I/O functions
+int TSnap7MicroClient::ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+ return PerformOperation();
+int TSnap7MicroClient::WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+ Job.Op = s7opWriteArea;
+int TSnap7MicroClient::ReadMultiVars(PS7DataItem Item, int ItemsCount)
+ Job.Op =s7opReadMultiVars;
+ Job.pData =Item;
+int TSnap7MicroClient::WriteMultiVars(PS7DataItem Item, int ItemsCount)
+ Job.Op =s7opWriteMultiVars;
+int TSnap7MicroClient::DBRead(int DBNumber, int Start, int Size, void * pUsrData)
+ return ReadArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::DBWrite(int DBNumber, int Start, int Size, void * pUsrData)
+ return WriteArea(S7AreaDB, DBNumber, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::MBRead(int Start, int Size, void * pUsrData)
+ return ReadArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::MBWrite(int Start, int Size, void * pUsrData)
+ return WriteArea(S7AreaMK, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::EBRead(int Start, int Size, void * pUsrData)
+ return ReadArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::EBWrite(int Start, int Size, void * pUsrData)
+ return WriteArea(S7AreaPE, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::ABRead(int Start, int Size, void * pUsrData)
+ return ReadArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::ABWrite(int Start, int Size, void * pUsrData)
+ return WriteArea(S7AreaPA, 0, Start, Size, S7WLByte, pUsrData);
+int TSnap7MicroClient::TMRead(int Start, int Amount, void * pUsrData)
+ return ReadArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
+int TSnap7MicroClient::TMWrite(int Start, int Amount, void * pUsrData)
+ return WriteArea(S7AreaTM, 0, Start, Amount, S7WLTimer, pUsrData);
+int TSnap7MicroClient::CTRead(int Start, int Amount, void * pUsrData)
+ return ReadArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
+int TSnap7MicroClient::CTWrite(int Start, int Amount, void * pUsrData)
+ return WriteArea(S7AreaCT, 0, Start, Amount, S7WLCounter, pUsrData);
+int TSnap7MicroClient::ListBlocks(PS7BlocksList pUsrData)
+ Job.Op =s7opListBlocks;
+int TSnap7MicroClient::GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData)
+int TSnap7MicroClient::GetPgBlockInfo(void * pBlock, PS7BlockInfo pUsrData, int Size)
+ int Result=CheckBlock(-1,-1,pBlock,Size);
+ Info=PS7CompactBlockInfo(pBlock);
+ pUsrData->BlkType =Info->SubBlkType;
+ pUsrData->BlkNumber=SwapWord(Info->BlkNum);
+ pUsrData->BlkLang =Info->BlkLang;
+ pUsrData->BlkFlags =Info->BlkFlags;
+ pUsrData->MC7Size =SwapWord(Info->MC7Len);
+ pUsrData->LoadSize =SwapDWord(Info->LenLoadMem);
+ pUsrData->LocalData=SwapDWord(Info->LocDataLen);
+ pUsrData->SBBLength=SwapDWord(Info->SbbLen);
+ pUsrData->CheckSum =0; // this info is not available
+ pUsrData->Version =0; // this info is not available
+ FillTime(SwapWord(Info->CodeTime_dy),pUsrData->CodeDate);
+ FillTime(SwapWord(Info->IntfTime_dy),pUsrData->IntfDate);
+ Footer=PS7BlockFooter(pbyte(Info)+pUsrData->LoadSize-sizeof(TS7BlockFooter));
+ memcpy(pUsrData->Author,Footer->Author,8);
+ memcpy(pUsrData->Family,Footer->Family,8);
+ memcpy(pUsrData->Header,Footer->Header,8);
+ return SetError(Result);
+int TSnap7MicroClient::ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount)
+ if (ItemsCount<1)
+int TSnap7MicroClient::Upload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+int TSnap7MicroClient::FullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+ Job.IParam =1; // header + data + footer
+int TSnap7MicroClient::Download(int BlockNum, void * pUsrData, int Size)
+int TSnap7MicroClient::Delete(int BlockType, int BlockNum)
+ Job.Op =s7opDelete;
+int TSnap7MicroClient::DBGet(int DBNumber, void * pUsrData, int & Size)
+int TSnap7MicroClient::DBFill(int DBNumber, int FillChar)
+int TSnap7MicroClient::GetPlcDateTime(tm &DateTime)
+ Job.Op =s7opGetDateTime;
+ Job.pData =&DateTime;
+int TSnap7MicroClient::SetPlcDateTime(tm * DateTime)
+ Job.Op =s7opSetDateTime;
+ Job.pData =DateTime;
+int TSnap7MicroClient::SetPlcSystemDateTime()
+ time_t Now;
+ time(&Now);
+ struct tm * DateTime = localtime (&Now);
+ return SetPlcDateTime(DateTime);
+int TSnap7MicroClient::GetOrderCode(PS7OrderCode pUsrData)
+ Job.Op =s7opGetOrderCode;
+int TSnap7MicroClient::GetCpuInfo(PS7CpuInfo pUsrData)
+ Job.Op =s7opGetCpuInfo;
+int TSnap7MicroClient::GetCpInfo(PS7CpInfo pUsrData)
+ Job.Op =s7opGetCpInfo;
+int TSnap7MicroClient::ReadSZL(int ID, int Index, PS7SZL pUsrData, int &Size)
+int TSnap7MicroClient::ReadSZLList(PS7SZLList pUsrData, int &ItemsCount)
+int TSnap7MicroClient::PlcHotStart()
+ Job.Op =s7opPlcHotStart;
+int TSnap7MicroClient::PlcColdStart()
+ Job.Op =s7opPlcColdStart;
+int TSnap7MicroClient::PlcStop()
+ Job.Op =s7opPlcStop;
+int TSnap7MicroClient::CopyRamToRom(int Timeout)
+int TSnap7MicroClient::Compress(int Timeout)
+int TSnap7MicroClient::GetPlcStatus(int & Status)
+ Job.Op =s7opGetPlcStatus;
+ Job.pData =&Status;
+int TSnap7MicroClient::GetProtection(PS7Protection pUsrData)
+ Job.Op =s7opGetProtection;
+int TSnap7MicroClient::SetSessionPassword(char *Password)
+ size_t L = strlen(Password);
+ // checks the len
+ if ((L<1) || (L>8))
+ // prepares an 8 char string filled with spaces
+ memset(&opData,0x20,8);
+ // copies
+ strncpy((char*)&opData,Password,L);
+ Job.Op =s7opSetPassword;
+int TSnap7MicroClient::ClearSessionPassword()
+ Job.Op =s7opClearPassword;
@@ -0,0 +1,364 @@
+#ifndef s7_micro_client_h
+#define s7_micro_client_h
+#include "s7_peer.h"
+const longword errCliMask = 0xFFF00000;
+const longword errCliBase = 0x000FFFFF;
+const time_t DeltaSecs = 441763200; // Seconds between 1970/1/1 (C time base) and 1984/1/1 (Siemens base)
+typedef int TS7ResultItems[MaxVars];
+typedef TS7ResultItems *PS7ResultItems;
+ int BlkType;
+ int BlkNumber;
+ int BlkLang;
+ int BlkFlags;
+ int LoadSize;
+ int LocalData;
+ int SBBLength;
+ int CheckSum;
+ int Version;
+ char CodeDate[11];
+ char IntfDate[11];
+ char Author[9];
+ char Family[9];
+ char Header[9];
+ char Code[21]; // Order Code
+ byte V1; // Version V1.V2.V3
+#define s7opNone 0
+#define s7opReadArea 1
+#define s7opWriteArea 2
+#define s7opReadMultiVars 3
+#define s7opWriteMultiVars 4
+#define s7opDBGet 5
+#define s7opUpload 6
+#define s7opDownload 7
+#define s7opDelete 8
+#define s7opListBlocks 9
+#define s7opAgBlockInfo 10
+#define s7opListBlocksOfType 11
+#define s7opReadSzlList 12
+#define s7opReadSZL 13
+#define s7opGetDateTime 14
+#define s7opSetDateTime 15
+#define s7opGetOrderCode 16
+#define s7opGetCpuInfo 17
+#define s7opGetCpInfo 18
+#define s7opGetPlcStatus 19
+#define s7opPlcHotStart 20
+#define s7opPlcColdStart 21
+#define s7opCopyRamToRom 22
+#define s7opCompress 23
+#define s7opPlcStop 24
+#define s7opGetProtection 25
+#define s7opSetPassword 26
+#define s7opClearPassword 27
+#define s7opDBFill 28
+// Param Number (to use with setparam)
+// Low level : change them to experiment new connections, their defaults normally work well
+const int pc_iso_SendTimeout = 6;
+const int pc_iso_RecvTimeout = 7;
+const int pc_iso_ConnTimeout = 8;
+const int pc_iso_SrcRef = 1;
+const int pc_iso_DstRef = 2;
+const int pc_iso_SrcTSAP = 3;
+const int pc_iso_DstTSAP = 4;
+const int pc_iso_IsoPduSize = 5;
+const word CONNTYPE_PG = 0x01; // Connect to the PLC as a PG
+const word CONNTYPE_OP = 0x02; // Connect to the PLC as an OP
+const word CONNTYPE_BASIC = 0x03; // Basic connection
+// Internal struct for operations
+// Commands are not executed directly in the function such as "DBRead(...",
+// but this struct is filled and then PerformOperation() is called.
+// This allow us to implement async function very easily.
+struct TSnap7Job
+ int Op; // Operation Code
+ int Result; // Operation result
+ bool Pending; // A Job is pending
+ longword Time; // Job Execution time
+ // Read/Write
+ int Area; // Also used for Block type and Block of type
+ int Number; // Used for DB Number, Block number
+ int Start; // Offset start
+ int WordLen; // Word length
+ // SZL
+ int ID; // SZL ID
+ int Index; // SZL Index
+ // ptr info
+ void * pData; // User data pointer
+ int Amount; // Items amount/Size in input
+ int *pAmount; // Items amount/Size in output
+ // Generic
+ int IParam; // Used for full upload and CopyRamToRom extended timeout
+class TSnap7MicroClient: public TSnap7Peer
+ void FillTime(word SiemensTime, char *PTime);
+ byte BCDtoByte(byte B);
+ byte WordToBCD(word Value);
+ int opReadArea();
+ int opWriteArea();
+ int opReadMultiVars();
+ int opWriteMultiVars();
+ int opListBlocks();
+ int opListBlocksOfType();
+ int opAgBlockInfo();
+ int opDBGet();
+ int opDBFill();
+ int opUpload();
+ int opDownload();
+ int opDelete();
+ int opReadSZL();
+ int opReadSZLList();
+ int opGetDateTime();
+ int opSetDateTime();
+ int opGetOrderCode();
+ int opGetCpuInfo();
+ int opGetCpInfo();
+ int opGetPlcStatus();
+ int opPlcStop();
+ int opPlcHotStart();
+ int opPlcColdStart();
+ int opCopyRamToRom();
+ int opCompress();
+ int opGetProtection();
+ int opSetPassword();
+ int opClearPassword();
+ int CpuError(int Error);
+ longword DWordAt(void * P);
+ int CheckBlock(int BlockType, int BlockNum, void *pBlock, int Size);
+ int SubBlockToBlock(int SBB);
+ word ConnectionType;
+ longword JobStart;
+ TSnap7Job Job;
+ int DataSizeByte(int WordLength);
+ int opSize; // last operation size
+ int PerformOperation();
+ TS7Buffer opData;
+ TSnap7MicroClient();
+ ~TSnap7MicroClient();
+ void SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTsap);
+ void SetConnectionType(word ConnType);
+ // Fundamental Data I/O functions
+ int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData);
+ int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData);
+ // Data I/O Helper functions
+ int DBRead(int DBNumber, int Start, int Size, void * pUsrData);
+ int DBWrite(int DBNumber, int Start, int Size, void * pUsrData);
+ int MBRead(int Start, int Size, void * pUsrData);
+ int MBWrite(int Start, int Size, void * pUsrData);
+ int EBRead(int Start, int Size, void * pUsrData);
+ int EBWrite(int Start, int Size, void * pUsrData);
+ int ABRead(int Start, int Size, void * pUsrData);
+ int ABWrite(int Start, int Size, void * pUsrData);
+ int TMRead(int Start, int Amount, void * pUsrData);
+ int TMWrite(int Start, int Amount, void * pUsrData);
+ int CTRead(int Start, int Amount, void * pUsrData);
+ int CTWrite(int Start, int Amount, void * pUsrData);
+ int GetPgBlockInfo(void * pBlock, PS7BlockInfo pUsrData, int Size);
+ int ListBlocksOfType(int BlockType, TS7BlocksOfType *pUsrData, int & ItemsCount);
+ int Upload(int BlockType, int BlockNum, void * pUsrData, int & Size);
+ int FullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size);
+ int Download(int BlockNum, void * pUsrData, int Size);
+ int DBGet(int DBNumber, void * pUsrData, int & Size);
+ int GetPlcDateTime(tm &DateTime);
+ int SetPlcDateTime(tm * DateTime);
+ int ReadSZL(int ID, int Index, PS7SZL pUsrData, int &Size);
+ int ReadSZLList(PS7SZLList pUsrData, int &ItemsCount);
+ int GetPlcStatus(int &Status);
+ bool Busy(){ return Job.Pending; };
+ int Time(){ return int(Job.Time);}
+typedef TSnap7MicroClient *PSnap7MicroClient;
+#endif // s7_micro_client_h
@@ -0,0 +1,1178 @@
+#include "s7_partner.h"
+static PServersManager ServersManager = NULL;
+int ServersManager_GetServer(longword BindAddress, PConnectionServer &Server)
+ if (ServersManager == NULL)
+ ServersManager = new TServersManager();
+ return ServersManager->GetServer(BindAddress, Server);
+void ServersManager_RemovePartner(PConnectionServer Server, PSnap7Partner Partner)
+ if (ServersManager != NULL)
+ ServersManager->RemovePartner(Server, Partner);
+ if (ServersManager->ServersCount==0)
+ delete ServersManager;
+ ServersManager = NULL;
+// CONNECTION SERVERS MANAGER
+TServersManager::TServersManager()
+ cs = new TSnapCriticalSection;
+ memset(Servers,0,sizeof(Servers));
+ ServersCount=0;
+TServersManager::~TServersManager()
+ Lock();
+ if (ServersCount>0)
+ for (c = 0; c < MaxAdapters; c++)
+ if (Servers[c]!=0)
+ delete Servers[c];
+ Servers[c]=0;
+ ServersCount--;
+ Unlock();
+ delete cs;
+void TServersManager::Lock()
+ cs->Enter();
+void TServersManager::Unlock()
+ cs->Leave();
+void TServersManager::AddServer(PConnectionServer Server)
+ if (Servers[c]==0)
+ Servers[c]=Server;
+ ServersCount++;
+int TServersManager::CreateServer(longword BindAddress, PConnectionServer &Server)
+ in_addr sin;
+ sin.s_addr=BindAddress;
+ if (ServersCount<MaxAdapters)
+ Server = new TConnectionServer();
+ Result=Server->StartTo(inet_ntoa(sin));
+ Server=0;
+ AddServer(Server);
+ return errServerNoRoom;
+int TServersManager::GetServer(longword BindAddress, PConnectionServer &Server)
+ for (c = 0; c < ServersCount; c++)
+ if (Servers[c]->LocalBind==BindAddress)
+ Server=Servers[c];
+ if (Server==0)
+ return CreateServer(BindAddress, Server);
+void TServersManager::RemovePartner(PConnectionServer Server, PSnap7Partner Partner)
+ Server->RemovePartner(Partner);
+ if (Server->PartnersCount==0)
+ if (Servers[c]==Server)
+// CONNECTION SERVER
+void TConnListenerThread::Execute()
+ socket_t Sock;
+ bool Valid;
+ if (FListener->CanRead(FListener->WorkInterval))
+ Sock=FListener->SckAccept(); // in any case we must accept
+ Valid=Sock!=INVALID_SOCKET;
+ // check if we are not destroying
+ if ((!Terminated) && (!FServer->Destroying))
+ if (Valid)
+ FServer->Incoming(Sock);
+ Msg_CloseSocket(Sock);
+TConnectionServer::TConnectionServer()
+ memset(Partners,0,sizeof(Partners));
+ FRunning = false;
+ PartnersCount = 0;
+TConnectionServer::~TConnectionServer()
+ Stop();
+void TConnectionServer::Lock()
+void TConnectionServer::Unlock()
+int TConnectionServer::Start()
+ // Creates the listener
+ SockListener = new TMsgSocket();
+ strncpy(SockListener->LocalAddress,FLocalAddress,16);
+ SockListener->LocalPort=isoTcpPort;
+ // Binds
+ Result=SockListener->SckBind();
+ LocalBind=SockListener->LocalBind;
+ // Listen
+ Result=SockListener->SckListen();
+ // Creates the Listener thread
+ ServerThread = new TConnListenerThread(SockListener, this);
+ ServerThread->Start();
+ delete SockListener;
+ FRunning=Result==0;
+int TConnectionServer::StartTo(const char *Address)
+ strncpy(FLocalAddress,Address,16);
+ return Start();
+void TConnectionServer::Stop()
+ if (FRunning)
+ // Kills the listener thread
+ ServerThread->Terminate();
+ if (ServerThread->WaitFor(csTimeout)!=WAIT_OBJECT_0)
+ ServerThread->Kill();
+ delete ServerThread;
+ // Kills the listener
+PSnap7Partner TConnectionServer::FindPartner(longword Address)
+ PSnap7Partner Result;
+ for (c = 0; c < MaxPartners; c++)
+ Result=Partners[c];
+ if (Result!=NULL)
+ if (Result->PeerAddress==Address)
+ return NULL;
+int TConnectionServer::FirstFree()
+ for (i = 0; i < MaxPartners; i++)
+ if (Partners[i]==0)
+ return i;
+ return -1;
+int TConnectionServer::RegisterPartner(PSnap7Partner Partner)
+ PSnap7Partner aPartner;
+ int idx;
+ // check if already exists a passive partner linked to the same peer address
+ aPartner=FindPartner(Partner->PeerAddress);
+ if (aPartner==NULL)
+ idx=FirstFree();
+ if (idx>=0)
+ Partners[idx]=Partner;
+ PartnersCount++;
+ return errParNoRoom;
+ return errParAddressInUse;
+void TConnectionServer::RemovePartner(PSnap7Partner Partner)
+ if (Partners[c]==Partner)
+ Partners[c]=0;
+ PartnersCount--;
+void TConnectionServer::Incoming(socket_t Sock)
+ PSnap7Partner Partner;
+ Address=Msg_GetSockAddr(Sock);
+ // Looks for a partner that is waiting for a connection from this address
+ Partner=FindPartner(Address);
+ // if partner exists must not be already connected : a partner can be connected
+ // with only one peer at time
+ if ((Partner!=NULL) && (!Partner->Stopping) && (!Partner->Connected))
+ Partner->SetSocket(Sock);
+ Msg_CloseSocket(Sock); // we are not interested
+// PARTHER THREAD
+void TPartnerThread::Execute()
+ longword TheTime;
+ FKaElapsed=SysGetTick();
+ while ((!Terminated) && (!FPartner->Destroying))
+ // Check connection
+ while (!Terminated && !FPartner->Connected && !FPartner->Destroying)
+ if (!FPartner->ConnectToPeer())
+ SysSleep(FRecoveryTime);
+ // Execution
+ if ((!Terminated) && (!FPartner->Destroying) && (!FPartner->Execute()))
+ // Keep Alive
+ if (!Terminated && (!FPartner->Destroying) && FPartner->Active && FPartner->Connected)
+ TheTime=SysGetTick();
+ if (TheTime-FKaElapsed>FPartner->KeepAliveTime)
+ FKaElapsed=TheTime;
+ if (!FPartner->Ping(FPartner->RemoteAddress))
+ FPartner->Disconnect();
+// S7 PARTNER
+TSnap7Partner::TSnap7Partner(bool CreateActive)
+ // We skip RFC/ISO header, our PDU is the ISO payload
+ PDUH_in=PS7ReqHeader(&PDU.Payload);
+ FWorkerThread=0;
+ OnBRecv = 0;
+ OnBSend = 0;
+ Active=CreateActive;
+ SendEvt = new TSnapEvent(true);
+ RecvEvt = new TSnapEvent(true);
+ FSendPending = false;
+ FRecvPending = false;
+ memset(&FRecvStatus,0,sizeof(TRecvStatus));
+ memset(&FRecvLast,0,sizeof(TRecvLast));
+ FSendElapsed = 0;
+ Destroying = false;
+ // public
+ Linked =false;
+ Running =false;
+ BindError =false;
+ BRecvTimeout =3000;
+ BSendTimeout =3000;
+ RecoveryTime =500;
+ KeepAliveTime =5000;
+ NextByte =0;
+ PeerAddress =0;
+ SendTime =0;
+ RecvTime =0;
+ BytesSent =0;
+ BytesRecv =0;
+ SendErrors =0;
+ RecvErrors =0;
+TSnap7Partner::~TSnap7Partner()
+ delete SendEvt;
+ delete RecvEvt;
+byte TSnap7Partner::GetNextByte()
+ NextByte++;
+ if (NextByte==0xFF)
+ NextByte=1;
+ return NextByte;
+int TSnap7Partner::Start()
+ PeerAddress=inet_addr(RemoteAddress);
+ SrcAddress =inet_addr(LocalAddress);
+ if (!Running)
+ if (!Active)
+ Result=ServersManager_GetServer(SrcAddress,FServer);
+ FServer->RegisterPartner(this);
+ BindError=Result!=0;
+ Linked=PeerConnect()==0;
+ Result=0; // we need to create the worker thread even tough it's not linked
+ // if ok create the worker thread
+ FWorkerThread = new TPartnerThread(this, RecoveryTime);
+ FWorkerThread->Start();
+ Running=Result==0;
+int TSnap7Partner::StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap)
+ SrcTSap=LocTsap;
+ DstTSap=RemTsap;
+ strcpy(LocalAddress,LocAddress);
+ strcpy(RemoteAddress,RemAddress);
+int TSnap7Partner::Stop()
+ if (Running)
+ Stopping=true; // to prevent incoming connections
+ CloseWorker();
+ if (!Active && (FServer!=0))
+ ServersManager_RemovePartner(FServer, this);
+ Stopping=false;
+ BindError=false;
+void TSnap7Partner::Disconnect()
+ Linked=false;
+int TSnap7Partner::GetParam(int ParamNumber, void * pValue)
+ case p_u16_LocalPort:
+ *Puint16_t(pValue)=LocalPort;
+ case p_i32_BSendTimeout:
+ *Pint32_t(pValue)=BSendTimeout;
+ case p_i32_BRecvTimeout:
+ *Pint32_t(pValue)=BRecvTimeout;
+ case p_u32_RecoveryTime:
+ *Puint32_t(pValue)=RecoveryTime;
+ case p_u32_KeepAliveTime:
+ *Puint32_t(pValue)=KeepAliveTime;
+ default: return errParInvalidParamNumber;
+int TSnap7Partner::SetParam(int ParamNumber, void * pValue)
+ if (!Connected && Active)
+ return errParCannotChangeParam;
+ BSendTimeout=*Pint32_t(pValue);
+ BRecvTimeout=*Pint32_t(pValue);
+ RecoveryTime=*Puint32_t(pValue);
+ KeepAliveTime=*Puint32_t(pValue);
+void TSnap7Partner::ClearRecv()
+ FRecvPending=false;
+bool TSnap7Partner::ConnectToPeer()
+ bool Result;
+ if (Active)
+ Result=PeerConnect()==0; // try to Connect
+ Linked=Result;
+ Result =false; // nothing : we are waiting for a connection
+bool TSnap7Partner::PerformFunctionNegotiate()
+ PReqFunNegotiateParams ReqParams;
+ PResFunNegotiateParams ResParams;
+ TS7Answer23 Answer;
+ // Setup pointers
+ ReqParams=PReqFunNegotiateParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+ ResParams=PResFunNegotiateParams(pbyte(&Answer)+sizeof(TS7ResHeader23));
+ // We are here only because we found a PduType_request, the partner can only
+ // handle Bs} requests and pdu negotiation requests.
+ // So, now we must check the incoming function
+ if (ReqParams->FunNegotiate!=pduNegotiate)
+ LastError=errParInvalidPDU;
+ // Prepares the answer
+ Answer.Header.P=0x32;
+ Answer.Header.PDUType =0x03;
+ Answer.Header.AB_EX =0x0000;
+ Answer.Header.Sequence=PDUH_in->Sequence;
+ Answer.Header.ParLen =SwapWord(sizeof(TResFunNegotiateParams));
+ Answer.Header.DataLen =0x0000;
+ Answer.Header.Error =0x0000;
+ // Params point at the } of the header
+ ResParams->FunNegotiate=pduNegotiate;
+ ResParams->Unknown=0x0;
+ // Checks PDU request length
+ if (SwapWord(ResParams->PDULength)>IsoPayload_Size)
+ ResParams->PDULength=SwapWord(IsoPayload_Size);
+ ResParams->PDULength=ReqParams->PDULength;
+ // We offer the same
+ ResParams->ParallelJobs_1=ReqParams->ParallelJobs_1;
+ ResParams->ParallelJobs_2=ReqParams->ParallelJobs_2;
+ // And store the value
+ PDULength=SwapWord(ResParams->PDULength);
+ // Sends the answer
+ Size=sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams);
+ if (isoSendBuffer(&Answer, Size)!=0)
+ SetError(errParNegotiatingPDU);
+ Linked=LastError==0;
+ return Linked;
+void TSnap7Partner::CloseWorker()
+ if (FWorkerThread)
+ FWorkerThread->Terminate();
+ if (FRecvPending || FSendPending)
+ if (FWorkerThread->WaitFor(Timeout)!=WAIT_OBJECT_0)
+ FWorkerThread->Kill();
+ delete FWorkerThread;
+bool TSnap7Partner::BlockSend()
+ PBSendReqParams ReqParams;
+ PBSendReqParams ResParams;
+ PBsendRequestData DataSendReq;
+ int TotalSize;
+ int SentSize;
+ int Slice;
+ int MaxSlice;
+ bool First, Last;
+ byte Seq_IN;
+ int TxIsoSize;
+ pbyte Data;
+ pword TotalPackSize;
+ int DataPtrOffset;
+ word Extra;
+ TotalSize=TxBuffer.Size;
+ SentSize =TotalSize;
+ Seq_IN=0x00;
+ // With BSend we can transfer up to 32k (S7300) or 64k (S7400), but splitted
+ // into slice that cannot exced the PDU size negotiated (including various headers).
+ MaxSlice=PDULength-sizeof(TS7ReqHeader)-sizeof(TBSendParams)-sizeof(TBsendRequestData)-2;
+ ReqParams=PBSendReqParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+ ResParams=ReqParams; // pdu 7 is symmetrical
+ while ((TotalSize>0) && (LastError==0))
+ Source=pbyte(&TxBuffer.Data)+Offset;
+ Slice=TotalSize;
+ TotalSize-=Slice;
+ Last=TotalSize==0;
+ // Prepare send
+ DataPtrOffset=sizeof(TS7ReqHeader)+sizeof(TBSendParams);
+ // Header
+ PDUH_out->PDUType=PduType_userdata; // 7
+ PDUH_out->Sequence=GetNextWord(); // Autoinc
+ PDUH_out->ParLen=SwapWord(sizeof(TBSendParams)); // 16 bytes
+ ReqParams->Plen =0x08; // length from here up the end of the record
+ ReqParams->Uk =0x12;
+ ReqParams->Tg =grBSend; // 0x46
+ ReqParams->SubFun =0x01;
+ ReqParams->Seq =Seq_IN;
+ ReqParams->Err =0x0000;
+ if (Last)
+ ReqParams->EoS =0x00;
+ ReqParams->EoS =0x01;
+ // Next byte is auto inc and not zero for partial sequences
+ // Is zero for lonely sequences.
+ if (First && Last)
+ ReqParams->IDSeq=0x00;
+ ReqParams->IDSeq=GetNextByte();
+ DataSendReq=PBsendRequestData(pbyte(PDUH_out)+DataPtrOffset);
+ // in the first pdu, after data header there is the whole packet length
+ TotalPackSize=pword(pbyte(DataSendReq)+sizeof(TBsendRequestData));
+ Data=pbyte(TotalPackSize)+sizeof(word);
+ *TotalPackSize=SwapWord(word(TxBuffer.Size));
+ Extra=2; // extra bytes (total pack size indicator)
+ Data=pbyte(DataSendReq)+sizeof(TBsendRequestData);
+ Extra=0;
+ PDUH_out->DataLen=SwapWord(word(sizeof(TBsendRequestData))+Slice+Extra);
+ DataSendReq->Len =SwapWord(Slice+8+Extra);
+ TxIsoSize=Slice+sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBsendRequestData)+Extra;
+ DataSendReq->FF =0xFF;
+ DataSendReq->TRSize =TS_ResOctet;
+ DataSendReq->DHead[0]=0x12;
+ DataSendReq->DHead[1]=0x06;
+ DataSendReq->DHead[2]=0x13;
+ DataSendReq->DHead[3]=0x00;
+ DataSendReq->R_ID =SwapDWord(TxBuffer.R_ID);
+ memcpy(Data, Source ,Slice);
+ if (isoExchangeBuffer(NULL, TxIsoSize)!=0)
+ SetError(errParSendingBlock);
+ if (LastError==0)
+ Seq_IN=ResParams->Seq;
+ if (SwapWord(ResParams->Err)!=0)
+ LastError=errParSendRefused;
+ First =false;
+ MaxSlice+=2; // only in the first frame we have the extra info
+ SendTime=SysGetTick()-FSendElapsed;
+ BytesSent+=SentSize;
+ return LastError==0;
+bool TSnap7Partner::PickData()
+ PBSendResData ResData;
+ pbyte Source, Target;
+ int AnswerLen;
+ ReqParams =PBSendReqParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+ ResParams =ReqParams; // pdu 7 is symmetrical
+ DataSendReq=PBsendRequestData(pbyte(ReqParams)+sizeof(TBSendParams));
+ // Checks if PDU is a BSend request
+ if ((PDUH_in->PDUType!=PduType_userdata) || (ReqParams->Tg!=grBSend))
+ if (FRecvStatus.First)
+ TotalPackSize=(word*)(pbyte(DataSendReq)+sizeof(TBsendRequestData));
+ FRecvStatus.TotalLength=SwapWord(*TotalPackSize);
+ Source=pbyte(DataSendReq)+sizeof(TBsendRequestData)+2;
+ FRecvStatus.In_R_ID=SwapDWord(DataSendReq->R_ID);
+ FRecvStatus.Offset=0;
+ Slice=SwapWord(DataSendReq->Len)-10;
+ Slice=SwapWord(DataSendReq->Len)-8;
+ Source=pbyte(DataSendReq)+sizeof(TBsendRequestData);
+ FRecvStatus.Done=ReqParams->EoS==0x00;
+ Target=pbyte(&RxBuffer)+FRecvStatus.Offset;
+ FRecvStatus.Offset+=Slice;
+ ResData =PBSendResData(pbyte(ResParams)+sizeof(TBSendParams));
+ // Send Answer
+ PDUH_out->ParLen =SwapWord(sizeof(TBSendParams));
+ PDUH_out->DataLen =SwapWord(sizeof(TBSendResData));
+ ResParams->Head[0]=0x00;
+ ResParams->Head[1]=0x01;
+ ResParams->Head[2]=0x12;
+ ResParams->Plen =0x08; // length from here up the end of the record
+ ResParams->Uk =0x12;
+ ResParams->Tg =0x86;
+ ResParams->SubFun =0x01;
+ ResParams->Seq =FRecvStatus.Seq_Out;
+ ResParams->Err =0x0000;
+ ResParams->EoS =0x00;
+ ResParams->IDSeq =0x00;
+ ResData->DHead[0] =0x0A;
+ ResData->DHead[1] =0x00;
+ ResData->DHead[2] =0x00;
+ ResData->DHead[3] =0x00;
+ AnswerLen=sizeof(TS7ReqHeader)+sizeof(TBSendParams)+sizeof(TBSendResData);
+ if (isoSendBuffer(NULL,AnswerLen)!=0)
+ SetError(errParRecvingBlock);
+bool TSnap7Partner::BlockRecv()
+ if (!FRecvPending) // Start sequence
+ FRecvPending=true;
+ FRecvStatus.First=true;
+ FRecvStatus.Done =false;
+ FRecvStatus.Seq_Out =GetNextByte();
+ FRecvStatus.Elapsed =SysGetTick();
+ FRecvLast.Done=false;
+ FRecvLast.Result=0;
+ FRecvLast.R_ID=0;
+ FRecvLast.Size=0;
+ FRecvLast.Count++;
+ if (FRecvLast.Count==0xFFFFFFFF)
+ FRecvLast.Count=0;
+ Result=PickData();
+ FRecvStatus.First=false;
+ if (!Result || FRecvStatus.Done)
+ FRecvLast.Result=LastError;
+ if (Result)
+ BytesRecv+=FRecvStatus.TotalLength;
+ RecvTime=SysGetTick()-FRecvStatus.Elapsed;
+ FRecvLast.R_ID=FRecvStatus.In_R_ID;
+ FRecvLast.Size=FRecvStatus.TotalLength;
+ RecvEvt->Set();
+ if ((OnBRecv!=NULL) && !Destroying)
+ OnBRecv(FRecvUsrPtr, FRecvLast.Result, FRecvLast.R_ID, &RxBuffer, FRecvLast.Size);
+ FRecvLast.Done=true;
+ ClearRecv();
+bool TSnap7Partner::ConnectionConfirm()
+ if (FRecvPending)
+ IsoConfirmConnection(pdu_type_CC); // <- Connection confirm
+ return LastTcpError!=WSAECONNRESET;
+int TSnap7Partner::Status()
+ if (Linked)
+ return par_receiving;
+ if (FSendPending)
+ return par_sending;
+ return par_linked;
+ return par_connecting;
+ return par_waiting;
+ else{
+ if ((!Active) && BindError)
+ return par_binderror;
+ return par_stopped;
+bool TSnap7Partner::Execute()
+ TPDUKind PduKind;
+ bool RTimeout;
+ bool Result =true;
+ // Checks if there is something to send (and we are not receiving...)
+ if (FSendPending && !FRecvPending)
+ Result=BlockSend();
+ SendEvt->Set();
+ if ((OnBSend!=NULL) && (!Destroying))
+ OnBSend(FSendUsrPtr, LastError);
+ FSendPending=false;
+ // Checks if there is something to recv
+ if (Result && CanRead(WorkInterval))
+ // Peeks info and returns PDU Kind
+ isoRecvPDU(&PDU);
+ // First check valid data incoming (most likely situation)
+ IsoPeek(&PDU,PduKind);
+ if (PduKind==pkValidData)
+ if (PDUH_in->PDUType==PduType_request)
+ Result=PerformFunctionNegotiate();
+ else // Pdu type userdata
+ Result=BlockRecv();
+ if (PduKind==pkConnectionRequest)
+ Result=ConnectionConfirm();
+ else // nothing else
+ Result=false;
+ if (LastTcpError==WSAECONNRESET)
+ if (!Result)
+ // Check BRecv sequence timeout
+ RTimeout= FRecvPending && (SysGetTick()-FRecvStatus.Elapsed>longword(BRecvTimeout));
+ if (RTimeout)
+ LastError=errParFrameTimeout;
+ OnBRecv(FRecvUsrPtr, LastError, 0, &RxBuffer,0);
+ if (!Result || RTimeout)
+ ClearRecv(); // parframetimeout
+int TSnap7Partner::BSend(longword R_ID, void *pUsrData, int Size)
+ // The block send is managed into the worker thread.
+ // Sync Bsend consists of AsBSend+WaitAsCompletion
+ int Result=AsBSend(R_ID, pUsrData, Size);
+ Result=WaitAsBSendCompletion(BSendTimeout);
+int TSnap7Partner::AsBSend(longword R_ID, void *pUsrData, int Size)
+ SendTime=0;
+ if (!FSendPending)
+ memcpy(&TxBuffer.Data, pUsrData, Size);
+ TxBuffer.R_ID=R_ID;
+ TxBuffer.Size=Size;
+ SendEvt->Reset();
+ FSendPending=true;
+ FSendElapsed=SysGetTick();
+ return errParBusy;
+ return SetError(errParNotLinked);
+bool TSnap7Partner::CheckAsBSendCompletion(int &opResult)
+ opResult=LastError;
+ opResult=errParBusy;
+ return !FSendPending;
+ opResult=errParDestroying;
+int TSnap7Partner::WaitAsBSendCompletion(longword Timeout)
+ if (SendEvt->WaitFor(BSendTimeout)==WAIT_OBJECT_0)
+ return SetError(errParDestroying);
+ return SetError(errParSendTimeout);
+int TSnap7Partner::SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr)
+ OnBSend=pCompletion;
+ FSendUsrPtr=usrPtr;
+int TSnap7Partner::BRecv(longword &R_ID, void *pData, int &Size, longword Timeout)
+ int Result=0;
+ if (RecvEvt->WaitFor(Timeout)==WAIT_OBJECT_0)
+ R_ID =FRecvLast.R_ID;
+ Size =FRecvLast.Size;
+ if (FRecvLast.Result==0)
+ if (pData!=NULL)
+ memcpy(pData, &RxBuffer, Size);
+ Result=errParInvalidParams;
+ Result=FRecvLast.Result;
+ RecvEvt->Reset();
+ Result=errParRecvTimeout;
+bool TSnap7Partner::CheckAsBRecvCompletion(int &opResult, longword &R_ID,
+ void *pData, int &Size)
+ Size=0;
+ bool Result=FRecvLast.Done;
+ Size=FRecvLast.Size;
+ R_ID=FRecvLast.R_ID;
+ opResult=FRecvLast.Result;
+ if ((pData!=NULL) && (Size>0) && (opResult==0))
+int TSnap7Partner::SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr)
+ OnBRecv=pCompletion;
+ FRecvUsrPtr=usrPtr;
@@ -0,0 +1,284 @@
+#ifndef s7_partner_h
+#define s7_partner_h
+using namespace std;
+#define MaxPartners 256
+#define MaxAdapters 256
+#define csTimeout 1500 // Connection server destruction timeout
+const int par_linked = 3; // running and connected
+const int par_binderror = 6; // error starting passive partner
+const longword errParMask = 0xFFF00000;
+const longword errParBase = 0x000FFFFF;
+class TSnap7Partner;
+typedef TSnap7Partner *PSnap7Partner;
+class TConnectionServer;
+typedef TConnectionServer *PConnectionServer;
+class TServersManager
+ PConnectionServer Servers[MaxAdapters];
+ TSnapCriticalSection *cs;
+ void Lock();
+ void Unlock();
+ int CreateServer(longword BindAddress, PConnectionServer &Server);
+ void AddServer(PConnectionServer Server);
+ int ServersCount;
+ TServersManager();
+ ~TServersManager();
+ int GetServer(longword BindAddress, PConnectionServer &Server);
+ void RemovePartner(PConnectionServer Server, PSnap7Partner Partner);
+typedef TServersManager *PServersManager;
+// CONNECTION SERVER (Don't inherit from TcpSrv to avoid dependence)
+class TConnListenerThread : public TSnapThread
+ TMsgSocket *FListener;
+ TConnectionServer *FServer;
+ TConnListenerThread(TMsgSocket *Listener, TConnectionServer *Server)
+ FServer=Server;
+ FListener=Listener;
+ FreeOnTerminate=false;
+typedef TConnListenerThread *PConnListenerThread;
+class TConnectionServer
+ bool FRunning;
+ // Bind Address
+ char FLocalAddress[16];
+ // Server listener
+ PConnListenerThread ServerThread;
+ // Socket listener
+ PMsgSocket SockListener;
+ // Finds a partner bound to the address
+ PSnap7Partner FindPartner(longword Address);
+ // Locks the Partner list
+ // Unlocks the Partner list
+ // Workers list
+ PSnap7Partner Partners[MaxPartners];
+ bool Destroying;
+ void Incoming(socket_t Sock);
+ int FirstFree();
+ int PartnersCount;
+ longword LocalBind;
+ TConnectionServer();
+ ~TConnectionServer();
+ void Stop();
+ int RegisterPartner(PSnap7Partner Partner);
+ void RemovePartner(PSnap7Partner Partner);
+ friend class TConnListenerThread;
+typedef TConnectionServer * PConnectionServer;
+// PARTNER THREAD
+class TPartnerThread : public TSnapThread
+ TSnap7Partner *FPartner;
+ longword FRecoveryTime;
+ longword FKaElapsed;
+ TPartnerThread(TSnap7Partner *Partner, longword RecoveryTime)
+ FPartner = Partner;
+ FRecoveryTime =RecoveryTime;
+ FreeOnTerminate =false;
+ ~TPartnerThread(){};
+typedef TPartnerThread *PPartnerThread;
+ bool Done;
+ longword TotalLength;
+ longword In_R_ID;
+ longword Elapsed;
+ byte Seq_Out;
+}TRecvStatus;
+ longword R_ID;
+ longword Count;
+}TRecvLast;
+typedef void (S7API *pfn_ParBRecvCallBack)(void * usrPtr, int opResult, longword R_ID, void *pdata, int Size);
+typedef void (S7API *pfn_ParBSendCompletion)(void * usrPtr, int opResult);
+class TSnap7Partner : public TSnap7Peer
+ PS7ReqHeader PDUH_in;
+ void *FRecvUsrPtr;
+ void *FSendUsrPtr;
+ PSnapEvent SendEvt;
+ PSnapEvent RecvEvt;
+ PConnectionServer FServer;
+ PPartnerThread FWorkerThread;
+ bool FSendPending;
+ bool FRecvPending;
+ TRecvStatus FRecvStatus;
+ TRecvLast FRecvLast;
+ TPendingBuffer TxBuffer;
+ TPendingBuffer RxBuffer;
+ longword FSendElapsed;
+ bool BindError;
+ byte NextByte;
+ pfn_ParBRecvCallBack OnBRecv;
+ pfn_ParBSendCompletion OnBSend;
+ void ClearRecv();
+ byte GetNextByte();
+ void CloseWorker();
+ bool BlockSend();
+ bool PickData();
+ bool BlockRecv();
+ bool ConnectionConfirm();
+ bool Stopping;
+ bool Execute();
+ void Disconnect();
+ bool ConnectToPeer();
+ bool PerformFunctionNegotiate();
+ bool Active;
+ bool Running;
+ longword PeerAddress;
+ longword SrcAddress;
+ int BRecvTimeout;
+ int BSendTimeout;
+ longword SendTime;
+ longword RecvTime;
+ longword RecoveryTime;
+ longword KeepAliveTime;
+ longword BytesSent;
+ longword BytesRecv;
+ longword SendErrors;
+ longword RecvErrors;
+ // The partner is linked when the init sequence is terminated
+ //(TCP connection + ISO connection + PDU Length negotiation)
+ bool Linked;
+ TSnap7Partner(bool CreateActive);
+ ~TSnap7Partner();
+ int StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap);
+ int GetParam(int ParamNumber, void * pValue);
+ int SetParam(int ParamNumber, void * pValue);
+ // Block send
+ bool CheckAsBSendCompletion(int &opResult);
+ int SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr);
+ // Block recv
+ int BRecv(longword &R_ID, void *pData, int &Size, longword Timeout);
+ bool CheckAsBRecvCompletion(int &opResult, longword &R_ID,
+ void *pData, int &Size);
+ int SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr);
+ friend class TConnectionServer;
+ friend class TPartnerThread;
+#endif // s7_partner_h
@@ -0,0 +1,122 @@
+TSnap7Peer::TSnap7Peer()
+ PDUH_out=PS7ReqHeader(&PDU.Payload);
+ PDURequest=480; // Our request, FPDULength will contain the CPU answer
+ LastError=0;
+ cntword = 0;
+TSnap7Peer::~TSnap7Peer()
+int TSnap7Peer::SetError(int Error)
+ LastError=Error | LastIsoError | LastTcpError;
+void TSnap7Peer::ClrError()
+word TSnap7Peer::GetNextWord()
+ if (cntword==0xFFFF)
+ cntword=0;
+ return cntword++;
+int TSnap7Peer::NegotiatePDULength( )
+ int Result, IsoSize = 0;
+ PReqFunNegotiateParams ReqNegotiate;
+ PResFunNegotiateParams ResNegotiate;
+ // Setup Pointers
+ ReqNegotiate = PReqFunNegotiateParams(pbyte(PDUH_out) + sizeof(TS7ReqHeader));
+ PDUH_out->P = 0x32; // Always $32
+ PDUH_out->PDUType = PduType_request; // $01
+ PDUH_out->AB_EX = 0x0000; // Always $0000
+ PDUH_out->ParLen = SwapWord(sizeof(TReqFunNegotiateParams)); // 8 bytes
+ PDUH_out->DataLen = 0x0000;
+ // Params
+ ReqNegotiate->FunNegotiate = pduNegotiate;
+ ReqNegotiate->Unknown = 0x00;
+ ReqNegotiate->ParallelJobs_1 = 0x0100;
+ ReqNegotiate->ParallelJobs_2 = 0x0100;
+ ReqNegotiate->PDULength = SwapWord(PDURequest);
+ IsoSize = sizeof( TS7ReqHeader ) + sizeof( TReqFunNegotiateParams );
+ Result = isoExchangeBuffer(NULL, IsoSize);
+ if ((Result == 0) && (IsoSize == int(sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams))))
+ ResNegotiate = PResFunNegotiateParams(pbyte(Answer) + sizeof(TS7ResHeader23));
+ if ( Answer->Error != 0 )
+ Result = SetError(errNegotiatingPDU);
+ if ( Result == 0 )
+ PDULength = SwapWord(ResNegotiate->PDULength);
+void TSnap7Peer::PeerDisconnect( )
+ isoDisconnect(true);
+int TSnap7Peer::PeerConnect( )
+ Result = isoConnect();
+ if (Result == 0)
+ Result = NegotiatePDULength();
+ if (Result != 0)
+#ifndef s7_peer_h
+#define s7_peer_h
+#include "s7_types.h"
+const longword errPeerMask = 0xFFF00000;
+const longword errPeerBase = 0x000FFFFF;
+class TSnap7Peer: public TIsoTcpSocket
+ word cntword;
+ PS7ReqHeader PDUH_out;
+ word GetNextWord();
+ int SetError(int Error);
+ int NegotiatePDULength();
+ void ClrError();
+ int PDULength;
+ int PDURequest;
+ TSnap7Peer();
+ ~TSnap7Peer();
+ void PeerDisconnect();
+ int PeerConnect();
@@ -0,0 +1,2159 @@
+#include "s7_server.h"
+#include "s7_firmware.h"
+const byte BitMask[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
+// ISO/TCP WORKER CLASS
+bool TIsoTcpWorker::IsoPerformCommand(int &Size)
+bool TIsoTcpWorker::ExecuteSend()
+bool TIsoTcpWorker::ExecuteRecv()
+ int PayloadSize;
+ if (CanRead(WorkInterval)) // should be Small to avoid time wait during the close
+ PayloadSize=PDUSize(&PDU)-DataHeaderSize;
+ return IsoPerformCommand(PayloadSize);
+ // Connection request incoming
+ // Disconnect request incoming (only for isotcp full complient equipment, not S7)
+ if (PduKind==pkDisconnectRequest)
+ IsoConfirmConnection(pdu_type_DC); // <- Disconnect confirm
+ // Empty fragment, maybe an ACK
+ if (PduKind==pkEmptyFragment)
+ PayloadSize=0;
+ // Valid PDU format but we have to discard it
+ if (PduKind==pkUnrecognizedType)
+ // Here we have an Invalid PDU
+bool TIsoTcpWorker::Execute()
+ return ExecuteSend() && ExecuteRecv();
+// S7 WORKER CLASS
+TS7Worker::TS7Worker()
+ // We skip RFC/ISO header, our PDU is the payload
+ PDUH_in =PS7ReqHeader(&PDU.Payload);
+ FPDULength=2048;
+ DBCnt =0;
+ LastBlk =Block_DB;
+bool TS7Worker::ExecuteRecv()
+ WorkInterval=FServer->WorkInterval;
+ return TIsoTcpWorker::ExecuteRecv();
+bool TS7Worker::CheckPDU_in(int PayloadSize)
+ // Checks the size : packet size must match with header infos
+ int Size=SwapWord(PDUH_in->ParLen)+SwapWord(PDUH_in->DataLen)+ReqHeaderSize;
+ if (Size!=PayloadSize)
+ // Checks PDUType : must be 1 or 7
+ if ((PDUH_in->PDUType!=PduType_request) &&
+ (PDUH_in->PDUType!=PduType_userdata))
+byte TS7Worker::BCD(word Value)
+ return ((Value / 10) << 4) + (Value % 10);
+void TS7Worker::FillTime(PS7Time PTime)
+ struct tm *DT = localtime (&Now);
+ PTime->bcd_year=BCD(DT->tm_year-100);
+ PTime->bcd_mon =BCD(DT->tm_mon+1);
+ PTime->bcd_day =BCD(DT->tm_mday);
+ PTime->bcd_hour=BCD(DT->tm_hour);
+ PTime->bcd_min =BCD(DT->tm_min);
+ PTime->bcd_sec =BCD(DT->tm_sec);
+ PTime->bcd_himsec=0;
+ PTime->bcd_dow =BCD(DT->tm_wday);
+void TS7Worker::DoEvent(longword Code, word RetCode, word Param1, word Param2,
+ word Param3, word Param4)
+ FServer->DoEvent(ClientHandle,Code,RetCode,Param1,Param2,Param3,Param4);
+void TS7Worker::DoReadEvent(longword Code, word RetCode, word Param1, word Param2,
+ FServer->DoReadEvent(ClientHandle,Code,RetCode,Param1,Param2,Param3,Param4);
+void TS7Worker::FragmentSkipped(int Size)
+// do nothing could be used for debug purpose
+bool TS7Worker::IsoPerformCommand(int &Size)
+ // Checks for Ack fragment
+ if (Size==0)
+ return PerformPDUAck(Size);
+ // First checks PDU consistence
+ if (CheckPDU_in(Size))
+ switch (PDUH_in->PDUType)
+ case PduType_request : return PerformPDURequest(Size);
+ case PduType_userdata : return PerformPDUUsrData(Size);
+ DoEvent(evcPDUincoming, evrMalformedPDU, Size, 0, 0, 0);
+bool TS7Worker::PerformPDUAck(int &Size)
+ // here we could keep track of ack empty fragment for debug purpose
+bool TS7Worker::PerformPDURequest(int &Size)
+ byte PDUFun;
+ bool Result = true;
+ // We have to store PDUfun since it will be overwritten
+ P = pbyte(PDUH_in)+ReqHeaderSize;
+ PDUFun=*P;
+ // Watches the function
+ switch (PDUFun)
+ case pduFuncRead : Result=PerformFunctionRead();
+ case pduFuncWrite : Result=PerformFunctionWrite();
+ case pduNegotiate : Result=PerformFunctionNegotiate();
+ case pduStart :
+ case pduStop : Result=PerformFunctionControl(PDUFun);
+ case pduStartUpload :
+ case pduUpload :
+ case pduEndUpload : Result=PerformFunctionUpload();
+ case pduReqDownload : Result=PerformFunctionDownload();
+ // <-- Further (custom) functions can be added here
+ DoEvent(evcPDUincoming, evrCannotHandlePDU, Size, 0, 0, 0);
+bool TS7Worker::PerformPDUUsrData(int &Size)
+ PS7ReqParams7 ReqParams;
+ byte Tg, SubFun;
+ // Set Pointer to request params
+ ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
+ Tg=ReqParams->Tg;
+ SubFun=ReqParams->SubFun;
+ // Switch type_group
+ switch (Tg)
+ case grProgrammer : Result=PerformGroupProgrammer();
+ case grCyclicData : Result=PerformGroupCyclicData();
+ case grBlocksInfo : Result=PerformGroupBlockInfo();
+ case grSZL : Result=PerformGroupSZL();
+ case grPassword : Result=PerformGroupSecurity();
+ case grClock : switch (SubFun)
+ case 0x01 : Result=PerformGetClock();
+ case 0x02 : Result=PerformSetClock();
+ DoEvent(evcPDUincoming, evrInvalidGroupUData, Tg, 0, 0, 0);
+int TS7Worker::DataSizeByte(int WordLength)
+// FUNCTION READ
+word TS7Worker::RA_NotFound(PResFunReadItem ResItem, TEv &EV)
+ ResItem->DataLength=SwapWord(0x0004);
+ ResItem->ReturnCode=Code7ResItemNotAvailable;
+ ResItem->TransportSize=0x00;
+ EV.EvRetCode=evrErrAreaNotFound;
+word TS7Worker::RA_OutOfRange(PResFunReadItem ResItem, TEv &EV)
+ ResItem->ReturnCode=Code7AddressOutOfRange;
+ EV.EvRetCode=evrErrOutOfRange;
+word TS7Worker::RA_SizeOverPDU(PResFunReadItem ResItem, TEv &EV)
+ ResItem->ReturnCode=byte(SwapWord(Code7DataOverPDU));
+ EV.EvRetCode=evrErrOverPDU;
+PS7Area TS7Worker::GetArea(byte S7Code, word index)
+ switch(S7Code)
+ case S7AreaPE : return FServer->HA[srvAreaPE];
+ case S7AreaPA : return FServer->HA[srvAreaPA];
+ case S7AreaMK : return FServer->HA[srvAreaMK];
+ case S7AreaCT : return FServer->HA[srvAreaCT];
+ case S7AreaTM : return FServer->HA[srvAreaTM];
+ case S7AreaDB : return FServer->FindDB(index);
+ default : return NULL;
+word TS7Worker::ReadArea(PResFunReadItem ResItemData, PReqFunReadItem ReqItemPar,
+ int &PDURemainder, TEv &EV)
+ PS7Area P;
+ word DBNum = 0;
+ word Elements;
+ longword Start, Size, ASize, AStart;
+ longword *PAdd;
+ byte BitIndex, ByteVal;
+ int Multiplier;
+ void *Source = NULL;
+ PSnapCriticalSection pcs;
+ P=NULL;
+ EV.EvStart =0;
+ EV.EvSize =0;
+ EV.EvRetCode =0;
+ EV.EvIndex =0;
+ EV.EvArea=ReqItemPar->Area;
+ // Get Pointer to selected Area
+ if (ReqItemPar->Area==S7AreaDB)
+ DBNum=SwapWord(ReqItemPar->DBNumber);
+ EV.EvIndex=DBNum;
+ if (!FServer->ResourceLess)
+ P = GetArea(ReqItemPar->Area, DBNum);
+ if (P == NULL)
+ return RA_NotFound(ResItemData, EV);
+ // Calcs the amount
+ Multiplier = DataSizeByte(ReqItemPar->TransportSize);
+ if (Multiplier==0)
+ return RA_OutOfRange(ResItemData, EV);
+ // Checks timers/counters coherence
+ if ((ReqItemPar->Area==S7AreaTM) ^ (ReqItemPar->TransportSize==S7WLTimer))
+ if ((ReqItemPar->Area==S7AreaCT) ^ (ReqItemPar->TransportSize==S7WLCounter))
+ // Calcs size
+ Elements = SwapWord(ReqItemPar->Length);
+ Size=Multiplier*Elements;
+ EV.EvSize=Size;
+ // The sum of the items must not exceed the PDU size negotiated
+ if (PDURemainder-Size<=0)
+ return RA_SizeOverPDU(ResItemData, EV);
+ PDURemainder-=Size;
+ // More then 1 bit is not supported by S7 CPU
+ if ((ReqItemPar->TransportSize==S7WLBit) && (Size>1))
+ // Calcs the start point
+ PAdd=(longword*)(&ReqItemPar->Area); // points to area since we need 4 bytes for a pointer
+ Start=SwapDWord(*PAdd & 0xFFFFFF00);
+ // Checks if the address is not multiple of 8 when transport size is neither bit nor timer nor counter
+ if (
+ (ReqItemPar->TransportSize!=S7WLBit) &&
+ (ReqItemPar->TransportSize!=S7WLTimer) &&
+ (ReqItemPar->TransportSize!=S7WLCounter) &&
+ ((Start % 8) !=0)
+ )
+ // AStart is only for callback
+ if ((ReqItemPar->TransportSize != S7WLBit) && (ReqItemPar->TransportSize != S7WLCounter) && (ReqItemPar->TransportSize != S7WLTimer))
+ AStart = Start >> 3;
+ AStart = Start;
+ if ((ReqItemPar->TransportSize == S7WLCounter) || (ReqItemPar->TransportSize == S7WLTimer))
+ Start = Start >> 1; // 1 Timer or Counter = 2 bytes
+ BitIndex =Start & 0x07; // start bit
+ Start =Start >> 3; // start byte
+ EV.EvStart=Start;
+ // Checks bounds
+ ASize = P->Size; // Area size
+ if (Start + Size > ASize)
+ Source = P->PData + Start;
+ // Read Event (before copy data)
+ DoReadEvent(evcDataRead,0,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+ if (FServer->ResourceLess)
+ memset(&ResItemData->Data, 0, Size);
+ if (!FServer->DoReadArea(ClientHandle, EV.EvArea, EV.EvIndex, AStart, Elements, ReqItemPar->TransportSize, &ResItemData->Data))
+ // Lock the area
+ pcs = P->cs;
+ pcs->Enter();
+ memcpy(&ResItemData->Data, Source, Size);
+ // Unlock the area
+ pcs->Leave();
+ ResItemData->ReturnCode=0xFF;
+ // Set Result transport size and, for bit, performs the mask
+ switch (ReqItemPar->TransportSize)
+ ByteVal=ResItemData->Data[0];
+ if ((ByteVal & BitMask[BitIndex])!=0)
+ ResItemData->Data[0]=0x01;
+ ResItemData->Data[0]=0x00;
+ ResItemData->TransportSize=TS_ResBit;
+ ResItemData->DataLength=SwapWord(Size);
+ };break;
+ case S7WLByte:
+ case S7WLWord:
+ case S7WLDWord:
+ ResItemData->TransportSize=TS_ResByte;
+ ResItemData->DataLength=SwapWord(Size*8);
+ ResItemData->TransportSize=TS_ResInt;
+ ResItemData->TransportSize=TS_ResReal;
+ case S7WLChar:
+ ResItemData->TransportSize=TS_ResOctet;
+ EV.EvRetCode=evrNoError;
+ return Size;
+bool TS7Worker::PerformFunctionRead()
+ word ItemSize;
+ int ItemsCount, c,
+ TotalSize,
+ PDURemainder;
+ TEv EV;
+ PDURemainder=FPDULength;
+ // Stage 1 : Setup pointers and initial check
+ ReqParams=PReqFunReadParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+ ResParams=PResFunReadParams(pbyte(&Answer)+ResHeaderSize23); // Params after the header
+ // trunk to 20 max items.
+ if (ReqParams->ItemsCount>MaxVars)
+ ReqParams->ItemsCount=MaxVars;
+ ItemsCount=ReqParams->ItemsCount;
+ // Stage 2 : gather data
+ Offset=sizeof(TResFunReadParams); // = 2
+ ResData[c]=PResFunReadItem(pbyte(ResParams)+Offset);
+ ItemSize=ReadArea(ResData[c],&ReqParams->Items[c],PDURemainder, EV);
+ // S7 doesn't xfer odd byte amount
+ if ((c<ItemsCount-1) && (ItemSize % 2 != 0))
+ ItemSize++;
+ Offset+=(ItemSize+4);
+ // For multiple items we have to create multiple events
+ if (ItemsCount>1)
+ DoEvent(evcDataRead,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+ // Stage 3 : finalize the answer and send the packet
+ Answer.Header.PDUType=0x03;
+ Answer.Header.AB_EX=0x0000;
+ Answer.Header.ParLen=SwapWord(sizeof(TResFunReadParams));
+ Answer.Header.Error=0x0000; // this is zero, we will find the error in ResData.ReturnCode
+ Answer.Header.DataLen=SwapWord(word(Offset)-2);
+ ResParams->FunRead =ReqParams->FunRead;
+ ResParams->ItemCount=ReqParams->ItemsCount;
+ TotalSize=ResHeaderSize23+int(Offset);
+ isoSendBuffer(&Answer, TotalSize);
+ // For single item (most likely case) it's better to work with the event after
+ // we sent the answer
+ if (ItemsCount==1)
+// FUNCTION WRITE
+byte TS7Worker::WA_NotFound(TEv &EV)
+ return Code7ResItemNotAvailable;
+byte TS7Worker::WA_InvalidTransportSize(TEv &EV)
+ EV.EvRetCode=evrErrTransportSize;
+ return Code7InvalidTransportSize;
+byte TS7Worker::WA_OutOfRange(TEv &EV)
+ return Code7AddressOutOfRange;
+byte TS7Worker::WA_DataSizeMismatch(TEv &EV)
+ EV.EvRetCode=evrDataSizeMismatch;
+ return Code7WriteDataSizeMismatch;
+byte TS7Worker::WriteArea(PReqFunWriteDataItem ReqItemData, PReqFunWriteItem ReqItemPar,
+ TEv &EV)
+ PS7Area P = NULL;
+ longword Start, Size, ASize, DataLen, AStart;
+ pbyte Target = NULL;
+ byte BitIndex;
+ EV.EvRetCode =evrNoError;
+ P=GetArea(ReqItemPar->Area, DBNum);
+ if (P==NULL)
+ return WA_NotFound(EV);
+ return WA_InvalidTransportSize(EV);
+ return WA_OutOfRange(EV);
+ Size = Multiplier*Elements;
+ // More) 1 bit is not supported by S7 CPU
+ // Calcs the start point ??
+ PAdd=(longword*)&ReqItemPar->Area; // points to area since we need 4 bytes for a pointer
+ BitIndex = Start & 0x07; // start bit
+ Start = Start >> 3; // start byte
+ EV.EvStart =Start;
+ Target = pbyte(P->PData + Start);
+ // Checks data size coherence
+ DataLen=SwapWord(ReqItemData->DataLength);
+ if ((ReqItemData->TransportSize!=TS_ResOctet) && (ReqItemData->TransportSize!=TS_ResReal) && (ReqItemData->TransportSize!=TS_ResBit))
+ DataLen=DataLen / 8;
+ if (DataLen!=Size)
+ return WA_DataSizeMismatch(EV);
+ if (!FServer->DoWriteArea(ClientHandle, EV.EvArea, EV.EvIndex, AStart, Elements, ReqItemPar->TransportSize, &ReqItemData->Data[0]))
+ if (ReqItemPar->TransportSize==S7WLBit)
+ if ((ReqItemData->Data[0] & 0x01) != 0) // bit set
+ *Target=*Target | BitMask[BitIndex];
+ else // bit reset
+ *Target=*Target & (~BitMask[BitIndex]);
+ // Write Data
+ memcpy(Target, &ReqItemData->Data[0], Size);
+ return 0xFF;
+bool TS7Worker::PerformFunctionWrite()
+ PResFunWrite ResData;
+ int L;
+ uintptr_t StartData;
+ int c, ItemsCount;
+ int ResDSize;
+ ReqParams=PReqFunWriteParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+ ResData =PResFunWrite(pbyte(&Answer)+ResHeaderSize23);
+ StartData=sizeof(TS7ReqHeader)+SwapWord(PDUH_in->ParLen);
+ ResDSize =ResHeaderSize23+2+ItemsCount;
+ ReqData[c]=PReqFunWriteDataItem(pbyte(PDUH_in)+StartData);
+ if ((ReqParams->Items[c].TransportSize == S7WLTimer) || (ReqParams->Items[c].TransportSize == S7WLCounter) || (ReqParams->Items[c].TransportSize == S7WLBit))
+ L = SwapWord(ReqData[c]->DataLength);
+ L = (SwapWord(ReqData[c]->DataLength) / 8);
+ StartData+=L+4;
+ // the datalength is always even
+ if ( L % 2 != 0) StartData++;
+ ResData->FunWrite =pduFuncWrite;
+ ResData->ItemCount=ReqParams->ItemsCount;
+ // Stage 2 : Write data
+ ResData->Data[c]=WriteArea(ReqData[c],&ReqParams->Items[c], EV);
+ DoEvent(evcDataWrite,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+ // Stage 3 : finalize the answer
+ Answer.Header.ParLen=SwapWord(0x02);
+ Answer.Header.Error=0x0000; // this is zero, we will find the error in ResData.ReturnCode if any
+ Answer.Header.DataLen=SwapWord(ItemsCount);
+ isoSendBuffer(&Answer,ResDSize);
+ // For single item (most likely case) it's better to fire the event after
+// FUNCTION NEGOTIATE
+bool TS7Worker::PerformFunctionNegotiate()
+ word ReqLen;
+ Answer.Header.ParLen=SwapWord(sizeof(TResFunNegotiateParams));
+ Answer.Header.DataLen=0x0000;
+ Answer.Header.Error=0x0000;
+ // Params point at the end of the header
+ if (FServer->ForcePDU == 0)
+ ReqLen = SwapWord(ReqParams->PDULength);
+ if (ReqLen<MinPduSize)
+ ResParams->PDULength = SwapWord(MinPduSize);
+ if (ReqLen>IsoPayload_Size)
+ ResParams->PDULength = SwapWord(IsoPayload_Size);
+ ResParams->PDULength = ReqParams->PDULength;
+ ResParams->PDULength = SwapWord(FServer->ForcePDU);
+ FPDULength=SwapWord(ResParams->PDULength); // Stores the value
+ isoSendBuffer(&Answer, Size);
+ // Store the event
+ DoEvent(evcNegotiatePDU, evrNoError, FPDULength, 0, 0, 0);
+// FUNCTION CONTROL
+bool TS7Worker::PerformFunctionControl(byte PduFun)
+ word ParLen;
+ word CtrlCode;
+ // Setup pointer
+ ResParams=PResFunCtrl(pbyte(&Answer)+sizeof(TS7ResHeader23));
+ Answer.Header.ParLen=SwapWord(0x0001); // We send only Res fun without para
+ ResParams->ResFun=PduFun;
+ ResParams->para =0x00;
+ ParLen=SwapWord(PDUH_in->ParLen);
+ if (PduFun==pduStop)
+ CtrlCode=CodeControlStop;
+ switch (ParLen)
+ case 16 : CtrlCode=CodeControlCompress; break;
+ case 18 : CtrlCode=CodeControlCpyRamRom; break;
+ case 20 : CtrlCode=CodeControlWarmStart; break;
+ case 22 : CtrlCode=CodeControlColdStart; break;
+ case 26 : CtrlCode=CodeControlInsDel; break;
+ default : CtrlCode=CodeControlUnknown;
+ isoSendBuffer(&Answer,sizeof(TS7ResHeader23)+1);
+ // Stores the event
+ DoEvent(evcControl,0,CtrlCode,0,0,0);
+ if ((CtrlCode==CodeControlWarmStart) || (CtrlCode==CodeControlColdStart))
+ FServer->CpuStatus=S7CpuStatusRun;
+ if (CtrlCode==CodeControlStop)
+ FServer->CpuStatus=S7CpuStatusStop;
+// FUNCTION UPLOAD
+bool TS7Worker::PerformFunctionUpload()
+ // Upload is not implemented, however to avoid that S7 manager hangs
+ // we simulate a cpu read/write protected.
+ // We can see the directory but can't upload/download anything
+ Answer.Header.PDUType =pduResponse;
+ Answer.Header.ParLen=0;
+ Answer.Header.DataLen=0;
+ Answer.Header.Error=SwapWord(Code7NeedPassword);
+ isoSendBuffer(&Answer,sizeof(TS7ResHeader23));
+ DoEvent(evcUpload,evrCannotUpload,evsStartUpload,0,0,0);
+// FUNCTION DOWNLOAD
+bool TS7Worker::PerformFunctionDownload()
+ // Download is not implemented, however to avoid that S7 manager hangs
+ DoEvent(evcUpload,evrCannotDownload, evsStartDownload,0,0,0);
+// FUNCTIONS PROGRAMMER AND CYCLIC DATA (NOT IMPLEMENTED...yet)
+bool TS7Worker::PerformGroupProgrammer()
+ DoEvent(evcPDUincoming,evrNotImplemented,grProgrammer,0,0,0);
+bool TS7Worker::PerformGroupCyclicData()
+ DoEvent(evcPDUincoming,evrNotImplemented,grCyclicData,0,0,0);
+// BLOCK(S) INFO FUNCTIONS
+void TS7Worker::BLK_ListAll(TCB &CB)
+ PDataFunListAll Data;
+ TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+sizeof(TDataFunListAll);
+ CB.Answer.Header.P=0x32;
+ CB.Answer.Header.PDUType=PduType_userdata;
+ CB.Answer.Header.AB_EX=0x0000;
+ CB.Answer.Header.Sequence=PDUH_in->Sequence;
+ CB.Answer.Header.ParLen =SwapWord(sizeof(TResFunGetBlockInfo));
+ CB.Answer.Header.DataLen=SwapWord(sizeof(TDataFunListAll));
+ CB.ResParams->Head[0]=CB.ReqParams->Head[0];
+ CB.ResParams->Head[1]=CB.ReqParams->Head[1];
+ CB.ResParams->Head[2]=CB.ReqParams->Head[2];
+ CB.ResParams->Plen =0x08;
+ CB.ResParams->Uk =0x12;
+ CB.ResParams->Tg =0x83; // Type response, group functions info
+ CB.ResParams->SubFun=SFun_ListAll;
+ CB.ResParams->Seq =CB.ReqParams->Seq;
+ CB.ResParams->Rsvd =0x0000;
+ CB.ResParams->ErrNo =0x0000;
+ Data=PDataFunListAll(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
+ Data->RetVal=0xFF;
+ Data->TRSize=TS_ResOctet;
+ Data->Length=SwapWord(28); // 28 = Size of TDataFunListAll.Blocks
+ // Fill elements, only DB will have a valid number
+ Data->Blocks[0].Zero=0x30;
+ Data->Blocks[0].BType=Block_OB;
+ Data->Blocks[0].BCount=0x0000; // We don't have OBs
+ Data->Blocks[1].Zero=0x30;
+ Data->Blocks[1].BType=Block_FB;
+ Data->Blocks[1].BCount=0x0000; // We don't have FBs
+ Data->Blocks[2].Zero=0x30;
+ Data->Blocks[2].BType=Block_FC;
+ Data->Blocks[2].BCount=0x0000; // We don't have FCs
+ Data->Blocks[3].Zero=0x30;
+ Data->Blocks[3].BType=Block_DB;
+ Data->Blocks[3].BCount=SwapWord(FServer->DBCount); // Most likely we HAVE DBs
+ Data->Blocks[4].Zero=0x30;
+ Data->Blocks[4].BType=Block_SDB;
+ Data->Blocks[4].BCount=0x0000; // We don't have SDBs
+ Data->Blocks[5].Zero=0x30;
+ Data->Blocks[5].BType=Block_SFC;
+ Data->Blocks[5].BCount=0x0000; // We don't have SFCs
+ Data->Blocks[6].Zero=0x30;
+ Data->Blocks[6].BType=Block_SFB;
+ Data->Blocks[6].BCount=0x0000; // We don't have SFBs
+ // Sends
+ isoSendBuffer(&CB.Answer,TotalSize);
+ DoEvent(evcDirectory, 0, evsGetBlockList, 0, 0, 0);
+void TS7Worker::BLK_NoResource_ListBoT(PDataFunGetBot Data, TCB &CB)
+ CB.DataLength =4;
+ DBCnt =0; // Reset counter
+ CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
+ CB.ResParams->ErrNo =0x0ED2; // function in error
+ Data->RetVal =0x0A; // No resource available
+ Data->TSize =0x00; // No transport size;
+ Data->DataLen =0x0000; // No data;
+ CB.evError =evrResNotFound;
+void TS7Worker::BLK_ListBoT(byte BlockType, bool Start, TCB &CB)
+ PDataFunGetBot Data;
+ int MaxItems, TotalSize, cnt;
+ int HiBound = FServer->DBLimit+1;
+ CB.evError=0;
+ MaxItems=(FPDULength - 32) / 4;
+ CB.ResParams->SubFun=SFun_ListBoT;
+ Data=PDataFunGetBot(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
+ if (BlockType==Block_DB)
+ cnt =0; // Local couter
+ if (Start)
+ DBCnt=-1; // Global counter
+ if (FServer->DBCount>0)
+ while ((cnt<MaxItems) && (DBCnt<HiBound))
+ DBCnt++;
+ if (FServer->DB[DBCnt]!=NULL)
+ Data->Items[cnt].BlockNum=SwapWord(FServer->DB[DBCnt]->Number);
+ Data->Items[cnt].Unknown =0x22;
+ Data->Items[cnt].BlockLang=0x05;
+ if ((cnt<MaxItems) || (DBCnt==HiBound))
+ DBCnt=0; // Finished
+ CB.ResParams->Rsvd=0x0023;
+ CB.ResParams->Rsvd=0x0123;
+ if (cnt>0)
+ Data->TSize =TS_ResOctet;
+ CB.DataLength=4+(cnt*word(sizeof(TDataFunGetBotItem)));
+ Data->DataLen=SwapWord(CB.DataLength-4);
+ BLK_NoResource_ListBoT(Data, CB);
+ else // we store only DBs
+ TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+CB.DataLength;
+ DoEvent(evcDirectory, CB.evError, evsStartListBoT, BlockType, 0, 0);
+ DoEvent(evcDirectory, CB.evError, evsListBoT, BlockType, 0, 0);
+void TS7Worker::BLK_NoResource_GetBlkInfo(PResDataBlockInfo Data, TCB &CB)
+ CB.ResParams->ErrNo =0x09D2; // function in error
+ Data->Length =0x0000; // No data;
+void TS7Worker::BLK_GetBlockNum_GetBlkInfo(int &BlkNum, PReqDataBlockInfo ReqData)
+ BlkNum = (ReqData->AsciiBlk[4] - 0x30) +
+ (ReqData->AsciiBlk[3] - 0x30) * 10 +
+ (ReqData->AsciiBlk[2] - 0x30) * 100 +
+ (ReqData->AsciiBlk[1] - 0x30) * 1000 +
+ (ReqData->AsciiBlk[0] - 0x30) * 10000;
+ if (BlkNum>65535)
+ BlkNum=-1;
+void TS7Worker::BLK_DoBlockInfo_GetBlkInfo(PS7Area DB, PResDataBlockInfo Data, TCB &CB)
+ CB.ResParams->SubFun=SFun_BlkInfo;
+ CB.DataLength =sizeof(TResDataBlockInfo);
+ Data->RetVal =0xFF;
+ Data->Length =SwapWord(78); // this struct - RetValData->Tsize and length
+ Data->Cst_b =0x01;
+ Data->BlkType =0x00;
+ Data->Cst_w1 =0x4A00;
+ Data->Cst_w2 =0x0022;
+ Data->Cst_pp =0x7070;
+ Data->Unknown_1 =0x01;
+ Data->BlkFlags =0x01;
+ Data->BlkLang =BlockLangDB;
+ Data->SubBlkType =0x0A;
+ Data->CodeTime_dy =SwapWord(5800);// Nov/18/1999 my princess's birthdate
+ Data->IntfTime_dy =Data->CodeTime_dy;
+ Data->LocDataLen =0x0000;
+ Data->BlkNumber =SwapWord(DB->Number);
+ Data->SbbLen =0x1400;
+ Data->AddLen =0x0000;
+ Data->MC7Len =SwapWord(DB->Size);
+ Data->LenLoadMem =SwapDWord(DB->Size+92);
+ Data->Version =0x01;
+ Data->Unknown_2 =0x00;
+ Data->BlkChksum =0x0000;
+void TS7Worker::BLK_GetBlkInfo(TCB &CB)
+ PResDataBlockInfo Data;
+ int BlkNum;
+ PS7Area BlkDB;
+ byte BlkTypeInfo;
+ Data =PResDataBlockInfo(pbyte(&CB.Answer)+ResHeaderSize17+sizeof(TResFunGetBlockInfo));
+ ReqData=PReqDataBlockInfo(pbyte(PDUH_in)+ReqHeaderSize+sizeof(TReqFunGetBlockInfo));
+ memset(Data,0,sizeof(TResDataBlockInfo)); // many fields are 0
+ BLK_GetBlockNum_GetBlkInfo(BlkNum, ReqData);
+ BlkTypeInfo=ReqData->BlkType;
+ if (BlkTypeInfo==Block_DB)
+ if (BlkNum>=0)
+ BlkDB=FServer->FindDB(BlkNum);
+ if (BlkDB!=NULL)
+ BLK_DoBlockInfo_GetBlkInfo(BlkDB, Data, CB);
+ BLK_NoResource_GetBlkInfo(Data, CB);
+ TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+sizeof(TResDataBlockInfo);
+ isoSendBuffer(&CB.Answer, TotalSize);
+ DoEvent(evcDirectory,CB.evError,evsGetBlockInfo,BlkTypeInfo,BlkNum,0);
+bool TS7Worker::PerformGroupBlockInfo()
+ TCB CB;
+ pbyte BlockType;
+ CB.ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_in)+ReqHeaderSize);
+ CB.ResParams=PResFunGetBlockInfo(pbyte(&CB.Answer)+ResHeaderSize17);
+ BlockType =pbyte(PDUH_in)+23;
+ switch (CB.ReqParams->SubFun)
+ case SFun_ListAll : BLK_ListAll(CB); break; // List all blocks
+ case SFun_ListBoT :
+ if (CB.ReqParams->Plen==4)
+ LastBlk=*BlockType;
+ BLK_ListBoT(*BlockType, true, CB); // start sequence from beginning
+ BLK_ListBoT(LastBlk, false, CB); // Continue sequence
+ }; break;
+ case SFun_BlkInfo : BLK_GetBlkInfo(CB); // Get Block info
+// FUNCTION SZL
+void TS7Worker::SZLNotAvailable()
+ SZL.Answer.Header.DataLen=SwapWord(sizeof(SZLNotAvail));
+ SZL.ResParams->Err = 0x02D4;
+ memcpy(SZL.ResData, &SZLNotAvail, sizeof(SZLNotAvail));
+ isoSendBuffer(&SZL.Answer,26);
+ SZL.SZLDone=false;
+void TS7Worker::SZLSystemState()
+ SZL.Answer.Header.DataLen=SwapWord(sizeof(SZLSysState));
+ SZL.ResParams->Err =0x0000;
+ memcpy(SZL.ResData,&SZLNotAvail,sizeof(SZLSysState));
+ isoSendBuffer(&SZL.Answer,28);
+ SZL.SZLDone=true;
+void TS7Worker::SZLData(void *P, int len)
+ int MaxSzl=FPDULength-22;
+ if (len>MaxSzl) {
+ len=MaxSzl;
+ SZL.Answer.Header.DataLen=SwapWord(word(len));
+ SZL.ResParams->resvd=0x0000; // this is the end, no more packets
+ memcpy(SZL.ResData, P, len);
+ SZL.ResData[2]=((len-4)>>8) & 0xFF;
+ SZL.ResData[3]=(len-4) & 0xFF;
+ isoSendBuffer(&SZL.Answer,22+len);
+// this block is dynamic (contains date/time and cpu status)
+void TS7Worker::SZL_ID424()
+ PS7Time PTime;
+ pbyte PStatus;
+ SZL.Answer.Header.DataLen=SwapWord(sizeof(SZL_ID_0424_IDX_XXXX));
+ PTime=PS7Time(pbyte(SZL.ResData)+24);
+ PStatus =pbyte(SZL.ResData)+15;
+ memcpy(SZL.ResData,&SZL_ID_0424_IDX_XXXX,sizeof(SZL_ID_0424_IDX_XXXX));
+ FillTime(PTime);
+ *PStatus=FServer->CpuStatus;
+ isoSendBuffer(&SZL.Answer,22+sizeof(SZL_ID_0424_IDX_XXXX));
+void TS7Worker::SZL_ID131_IDX003()
+ word len = sizeof(SZL_ID_0131_IDX_0003);
+ SZL.Answer.Header.DataLen=SwapWord(len);
+ memcpy(SZL.ResData, &SZL_ID_0131_IDX_0003, len);
+ // Set the max consistent data window to PDU size
+ SZL.ResData[18]=((FPDULength)>>8) & 0xFF;
+ SZL.ResData[19]=(FPDULength) & 0xFF;
+bool TS7Worker::PerformGroupSZL()
+ SZL.ReqParams=PReqFunReadSZLFirst(pbyte(PDUH_in)+ReqHeaderSize);
+ SZL.ResParams=PS7ResParams7(pbyte(&SZL.Answer)+ResHeaderSize17);
+ SZL.ResData =pbyte(&SZL.Answer)+ResHeaderSize17+sizeof(TS7Params7);
+ // Prepare Answer header
+ SZL.Answer.Header.P=0x32;
+ SZL.Answer.Header.PDUType=PduType_userdata;
+ SZL.Answer.Header.AB_EX=0x0000;
+ SZL.Answer.Header.Sequence=PDUH_in->Sequence;
+ SZL.Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
+ SZL.ResParams->Head[0]=SZL.ReqParams->Head[0];
+ SZL.ResParams->Head[1]=SZL.ReqParams->Head[1];
+ SZL.ResParams->Head[2]=SZL.ReqParams->Head[2];
+ SZL.ResParams->Plen =0x08;
+ SZL.ResParams->Uk =0x12;
+ SZL.ResParams->Tg =0x84; // Type response + group szl
+ SZL.ResParams->SubFun=SZL.ReqParams->SubFun;
+ SZL.ResParams->Seq =SZL.ReqParams->Seq;
+ // only two subfunction are defined : 0x01 read, 0x02 system state
+ if (SZL.ResParams->SubFun==0x02) // 0x02 = subfunction system state
+ SZLSystemState();
+ if (SZL.ResParams->SubFun!=0x01)
+ SZLNotAvailable();
+ // From here we assume subfunction = 0x01
+ SZL.ReqData=PS7ReqSZLData(pbyte(PDUH_in)+ReqHeaderSize+sizeof(TReqFunReadSZLFirst));// Data after params
+ SZL.ID=SwapWord(SZL.ReqData->ID);
+ SZL.Index=SwapWord(SZL.ReqData->Index);
+ // Switch prebuilt Data Bank (they come from a physical CPU)
+ switch (SZL.ID)
+ case 0x0000 : SZLData(&SZL_ID_0000_IDX_XXXX,sizeof(SZL_ID_0000_IDX_XXXX));break;
+ case 0x0F00 : SZLData(&SZL_ID_0F00_IDX_XXXX,sizeof(SZL_ID_0F00_IDX_XXXX));break;
+ case 0x0002 : SZLData(&SZL_ID_0002_IDX_XXXX,sizeof(SZL_ID_0002_IDX_XXXX));break;
+ case 0x0011 : SZLData(&SZL_ID_0011_IDX_XXXX,sizeof(SZL_ID_0011_IDX_XXXX));break;
+ case 0x0012 : SZLData(&SZL_ID_0012_IDX_XXXX,sizeof(SZL_ID_0012_IDX_XXXX));break;
+ case 0x0013 : SZLData(&SZL_ID_0013_IDX_XXXX,sizeof(SZL_ID_0013_IDX_XXXX));break;
+ case 0x0014 : SZLData(&SZL_ID_0014_IDX_XXXX,sizeof(SZL_ID_0014_IDX_XXXX));break;
+ case 0x0015 : SZLData(&SZL_ID_0015_IDX_XXXX,sizeof(SZL_ID_0015_IDX_XXXX));break;
+ case 0x0F14 : SZLData(&SZL_ID_0F14_IDX_XXXX,sizeof(SZL_ID_0F14_IDX_XXXX));break;
+ case 0x0019 : SZLData(&SZL_ID_0019_IDX_XXXX,sizeof(SZL_ID_0019_IDX_XXXX));break;
+ case 0x0F19 : SZLData(&SZL_ID_0F19_IDX_XXXX,sizeof(SZL_ID_0F19_IDX_XXXX));break;
+ case 0x001C : SZLData(&SZL_ID_001C_IDX_XXXX,sizeof(SZL_ID_001C_IDX_XXXX));break;
+ case 0x0F1C : SZLData(&SZL_ID_0F1C_IDX_XXXX,sizeof(SZL_ID_0F1C_IDX_XXXX));break;
+ case 0x0036 : SZLData(&SZL_ID_0036_IDX_XXXX,sizeof(SZL_ID_0036_IDX_XXXX));break;
+ case 0x0F36 : SZLData(&SZL_ID_0F36_IDX_XXXX,sizeof(SZL_ID_0F36_IDX_XXXX));break;
+ case 0x0025 : SZLData(&SZL_ID_0025_IDX_XXXX,sizeof(SZL_ID_0025_IDX_XXXX));break;
+ case 0x0F25 : SZLData(&SZL_ID_0F25_IDX_XXXX,sizeof(SZL_ID_0F25_IDX_XXXX));break;
+ case 0x0037 : SZLData(&SZL_ID_0037_IDX_XXXX,sizeof(SZL_ID_0037_IDX_XXXX));break;
+ case 0x0F37 : SZLData(&SZL_ID_0F37_IDX_XXXX,sizeof(SZL_ID_0F37_IDX_XXXX));break;
+ case 0x0074 : SZLData(&SZL_ID_0074_IDX_XXXX,sizeof(SZL_ID_0074_IDX_XXXX));break;
+ case 0x0F74 : SZLData(&SZL_ID_0F74_IDX_XXXX,sizeof(SZL_ID_0F74_IDX_XXXX));break;
+ case 0x0591 : SZLData(&SZL_ID_0591_IDX_XXXX,sizeof(SZL_ID_0591_IDX_XXXX));break;
+ case 0x0A91 : SZLData(&SZL_ID_0A91_IDX_XXXX,sizeof(SZL_ID_0A91_IDX_XXXX));break;
+ case 0x0F92 : SZLData(&SZL_ID_0F92_IDX_XXXX,sizeof(SZL_ID_0F92_IDX_XXXX));break;
+ case 0x0294 : SZLData(&SZL_ID_0294_IDX_XXXX,sizeof(SZL_ID_0294_IDX_XXXX));break;
+ case 0x0794 : SZLData(&SZL_ID_0794_IDX_XXXX,sizeof(SZL_ID_0794_IDX_XXXX));break;
+ case 0x0F94 : SZLData(&SZL_ID_0F94_IDX_XXXX,sizeof(SZL_ID_0F94_IDX_XXXX));break;
+ case 0x0095 : SZLData(&SZL_ID_0095_IDX_XXXX,sizeof(SZL_ID_0095_IDX_XXXX));break;
+ case 0x0F95 : SZLData(&SZL_ID_0F95_IDX_XXXX,sizeof(SZL_ID_0F95_IDX_XXXX));break;
+ case 0x00A0 : SZLData(&SZL_ID_00A0_IDX_XXXX,sizeof(SZL_ID_00A0_IDX_XXXX));break;
+ case 0x0FA0 : SZLData(&SZL_ID_0FA0_IDX_XXXX,sizeof(SZL_ID_0FA0_IDX_XXXX));break;
+ case 0x0017 : SZLData(&SZL_ID_0017_IDX_XXXX,sizeof(SZL_ID_0017_IDX_XXXX));break;
+ case 0x0F17 : SZLData(&SZL_ID_0F17_IDX_XXXX,sizeof(SZL_ID_0F17_IDX_XXXX));break;
+ case 0x0018 : SZLData(&SZL_ID_0018_IDX_XXXX,sizeof(SZL_ID_0018_IDX_XXXX));break;
+ case 0x0F18 : SZLData(&SZL_ID_0F18_IDX_XXXX,sizeof(SZL_ID_0F18_IDX_XXXX));break;
+ case 0x001A : SZLData(&SZL_ID_001A_IDX_XXXX,sizeof(SZL_ID_001A_IDX_XXXX));break;
+ case 0x0F1A : SZLData(&SZL_ID_0F1A_IDX_XXXX,sizeof(SZL_ID_0F1A_IDX_XXXX));break;
+ case 0x001B : SZLData(&SZL_ID_001B_IDX_XXXX,sizeof(SZL_ID_001B_IDX_XXXX));break;
+ case 0x0F1B : SZLData(&SZL_ID_0F1B_IDX_XXXX,sizeof(SZL_ID_0F1B_IDX_XXXX));break;
+ case 0x0021 : SZLData(&SZL_ID_0021_IDX_XXXX,sizeof(SZL_ID_0021_IDX_XXXX));break;
+ case 0x0A21 : SZLData(&SZL_ID_0A21_IDX_XXXX,sizeof(SZL_ID_0A21_IDX_XXXX));break;
+ case 0x0F21 : SZLData(&SZL_ID_0F21_IDX_XXXX,sizeof(SZL_ID_0F21_IDX_XXXX));break;
+ case 0x0023 : SZLData(&SZL_ID_0023_IDX_XXXX,sizeof(SZL_ID_0023_IDX_XXXX));break;
+ case 0x0F23 : SZLData(&SZL_ID_0F23_IDX_XXXX,sizeof(SZL_ID_0F23_IDX_XXXX));break;
+ case 0x0024 : SZLData(&SZL_ID_0024_IDX_XXXX,sizeof(SZL_ID_0024_IDX_XXXX));break;
+ case 0x0124 : SZLData(&SZL_ID_0124_IDX_XXXX,sizeof(SZL_ID_0124_IDX_XXXX));break;
+ case 0x0424 : SZL_ID424();break;
+ case 0x0038 : SZLData(&SZL_ID_0038_IDX_XXXX,sizeof(SZL_ID_0038_IDX_XXXX));break;
+ case 0x0F38 : SZLData(&SZL_ID_0F38_IDX_XXXX,sizeof(SZL_ID_0F38_IDX_XXXX));break;
+ case 0x003A : SZLData(&SZL_ID_003A_IDX_XXXX,sizeof(SZL_ID_003A_IDX_XXXX));break;
+ case 0x0F3A : SZLData(&SZL_ID_0F3A_IDX_XXXX,sizeof(SZL_ID_0F3A_IDX_XXXX));break;
+ case 0x0F9A : SZLData(&SZL_ID_0F9A_IDX_XXXX,sizeof(SZL_ID_0F9A_IDX_XXXX));break;
+ case 0x0D91 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0D91_IDX_0000,sizeof(SZL_ID_0D91_IDX_0000));break;
+ default: SZLNotAvailable();break;
+ case 0x0092 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0092_IDX_0000,sizeof(SZL_ID_0092_IDX_0000));break;
+ default : SZLNotAvailable();break;
+ case 0x0292 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0292_IDX_0000,sizeof(SZL_ID_0292_IDX_0000));break;
+ case 0x0692 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0692_IDX_0000,sizeof(SZL_ID_0692_IDX_0000));break;
+ case 0x0094 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0094_IDX_0000,sizeof(SZL_ID_0094_IDX_0000));break;
+ case 0x0D97 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0D97_IDX_0000,sizeof(SZL_ID_0D97_IDX_0000));break;
+ case 0x0111 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0111_IDX_0001,sizeof(SZL_ID_0111_IDX_0001));break;
+ case 0x0006 : SZLData(&SZL_ID_0111_IDX_0006,sizeof(SZL_ID_0111_IDX_0006));break;
+ case 0x0007 : SZLData(&SZL_ID_0111_IDX_0007,sizeof(SZL_ID_0111_IDX_0007));break;
+ case 0x0F11 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0F11_IDX_0001,sizeof(SZL_ID_0F11_IDX_0001));break;
+ case 0x0006 : SZLData(&SZL_ID_0F11_IDX_0006,sizeof(SZL_ID_0F11_IDX_0006));break;
+ case 0x0007 : SZLData(&SZL_ID_0F11_IDX_0007,sizeof(SZL_ID_0F11_IDX_0007));break;
+ case 0x0112 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0112_IDX_0000,sizeof(SZL_ID_0112_IDX_0000));break;
+ case 0x0100 : SZLData(&SZL_ID_0112_IDX_0100,sizeof(SZL_ID_0112_IDX_0100));break;
+ case 0x0200 : SZLData(&SZL_ID_0112_IDX_0200,sizeof(SZL_ID_0112_IDX_0200));break;
+ case 0x0400 : SZLData(&SZL_ID_0112_IDX_0400,sizeof(SZL_ID_0112_IDX_0400));break;
+ case 0x0F12 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0F12_IDX_0000,sizeof(SZL_ID_0F12_IDX_0000));break;
+ case 0x0100 : SZLData(&SZL_ID_0F12_IDX_0100,sizeof(SZL_ID_0F12_IDX_0100));break;
+ case 0x0200 : SZLData(&SZL_ID_0F12_IDX_0200,sizeof(SZL_ID_0F12_IDX_0200));break;
+ case 0x0400 : SZLData(&SZL_ID_0F12_IDX_0400,sizeof(SZL_ID_0F12_IDX_0400));break;
+ case 0x0113 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0113_IDX_0001,sizeof(SZL_ID_0113_IDX_0001));break;
+ case 0x0115 : switch(SZL.Index){
+ case 0x0800 : SZLData(&SZL_ID_0115_IDX_0800,sizeof(SZL_ID_0115_IDX_0800));break;
+ case 0x011C : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_011C_IDX_0001,sizeof(SZL_ID_011C_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_011C_IDX_0002,sizeof(SZL_ID_011C_IDX_0002));break;
+ case 0x0003 : SZLData(&SZL_ID_011C_IDX_0003,sizeof(SZL_ID_011C_IDX_0003));break;
+ case 0x0004 : SZLData(&SZL_ID_011C_IDX_0004,sizeof(SZL_ID_011C_IDX_0004));break;
+ case 0x0005 : SZLData(&SZL_ID_011C_IDX_0005,sizeof(SZL_ID_011C_IDX_0005));break;
+ case 0x0007 : SZLData(&SZL_ID_011C_IDX_0007,sizeof(SZL_ID_011C_IDX_0007));break;
+ case 0x0008 : SZLData(&SZL_ID_011C_IDX_0008,sizeof(SZL_ID_011C_IDX_0008));break;
+ case 0x0009 : SZLData(&SZL_ID_011C_IDX_0009,sizeof(SZL_ID_011C_IDX_0009));break;
+ case 0x000A : SZLData(&SZL_ID_011C_IDX_000A,sizeof(SZL_ID_011C_IDX_000A));break;
+ case 0x000B : SZLData(&SZL_ID_011C_IDX_000B,sizeof(SZL_ID_011C_IDX_000B));break;
+ case 0x0222 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0222_IDX_0001,sizeof(SZL_ID_0222_IDX_0001));break;
+ case 0x000A : SZLData(&SZL_ID_0222_IDX_000A,sizeof(SZL_ID_0222_IDX_000A));break;
+ case 0x0014 : SZLData(&SZL_ID_0222_IDX_0014,sizeof(SZL_ID_0222_IDX_0014));break;
+ case 0x0028 : SZLData(&SZL_ID_0222_IDX_0028,sizeof(SZL_ID_0222_IDX_0028));break;
+ case 0x0050 : SZLData(&SZL_ID_0222_IDX_0050,sizeof(SZL_ID_0222_IDX_0050));break;
+ case 0x0064 : SZLData(&SZL_ID_0222_IDX_0064,sizeof(SZL_ID_0222_IDX_0064));break;
+ case 0x0125 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0125_IDX_0000,sizeof(SZL_ID_0125_IDX_0000));break;
+ case 0x0001 : SZLData(&SZL_ID_0125_IDX_0001,sizeof(SZL_ID_0125_IDX_0001));break;
+ case 0x0225 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0225_IDX_0001,sizeof(SZL_ID_0225_IDX_0001));break;
+ case 0x0131 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0131_IDX_0001,sizeof(SZL_ID_0131_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_0131_IDX_0002,sizeof(SZL_ID_0131_IDX_0002));break;
+ case 0x0003 : SZL_ID131_IDX003();break;
+ case 0x0004 : SZLData(&SZL_ID_0131_IDX_0004,sizeof(SZL_ID_0131_IDX_0004));break;
+ case 0x0005 : SZLData(&SZL_ID_0131_IDX_0005,sizeof(SZL_ID_0131_IDX_0005));break;
+ case 0x0006 : SZLData(&SZL_ID_0131_IDX_0006,sizeof(SZL_ID_0131_IDX_0006));break;
+ case 0x0007 : SZLData(&SZL_ID_0131_IDX_0007,sizeof(SZL_ID_0131_IDX_0007));break;
+ case 0x0008 : SZLData(&SZL_ID_0131_IDX_0008,sizeof(SZL_ID_0131_IDX_0008));break;
+ case 0x0009 : SZLData(&SZL_ID_0131_IDX_0009,sizeof(SZL_ID_0131_IDX_0009));break;
+ case 0x0117 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0117_IDX_0000,sizeof(SZL_ID_0117_IDX_0000));break;
+ case 0x0001 : SZLData(&SZL_ID_0117_IDX_0001,sizeof(SZL_ID_0117_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_0117_IDX_0002,sizeof(SZL_ID_0117_IDX_0002));break;
+ case 0x0003 : SZLData(&SZL_ID_0117_IDX_0003,sizeof(SZL_ID_0117_IDX_0003));break;
+ case 0x0004 : SZLData(&SZL_ID_0117_IDX_0004,sizeof(SZL_ID_0117_IDX_0004));break;
+ case 0x0118 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_0118_IDX_0000,sizeof(SZL_ID_0118_IDX_0000));break;
+ case 0x0001 : SZLData(&SZL_ID_0118_IDX_0001,sizeof(SZL_ID_0118_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_0118_IDX_0002,sizeof(SZL_ID_0118_IDX_0002));break;
+ case 0x0003 : SZLData(&SZL_ID_0118_IDX_0003,sizeof(SZL_ID_0118_IDX_0003));break;
+ case 0x0132 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0132_IDX_0001,sizeof(SZL_ID_0132_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_0132_IDX_0002,sizeof(SZL_ID_0132_IDX_0002));break;
+ case 0x0003 : SZLData(&SZL_ID_0132_IDX_0003,sizeof(SZL_ID_0132_IDX_0003));break;
+ case 0x0004 : SZLData(&SZL_ID_0132_IDX_0004,sizeof(SZL_ID_0132_IDX_0004));break;
+ case 0x0005 : SZLData(&SZL_ID_0132_IDX_0005,sizeof(SZL_ID_0132_IDX_0005));break;
+ case 0x0006 : SZLData(&SZL_ID_0132_IDX_0006,sizeof(SZL_ID_0132_IDX_0006));break;
+ case 0x0007 : SZLData(&SZL_ID_0132_IDX_0007,sizeof(SZL_ID_0132_IDX_0007));break;
+ case 0x0008 : SZLData(&SZL_ID_0132_IDX_0008,sizeof(SZL_ID_0132_IDX_0008));break;
+ case 0x0009 : SZLData(&SZL_ID_0132_IDX_0009,sizeof(SZL_ID_0132_IDX_0009));break;
+ case 0x000A : SZLData(&SZL_ID_0132_IDX_000A,sizeof(SZL_ID_0132_IDX_000A));break;
+ case 0x000B : SZLData(&SZL_ID_0132_IDX_000B,sizeof(SZL_ID_0132_IDX_000B));break;
+ case 0x000C : SZLData(&SZL_ID_0132_IDX_000C,sizeof(SZL_ID_0132_IDX_000C));break;
+ case 0x0137 : switch(SZL.Index){
+ case 0x07FE : SZLData(&SZL_ID_0137_IDX_07FE,sizeof(SZL_ID_0137_IDX_07FE));break;
+ case 0x01A0 : switch(SZL.Index){
+ case 0x0000 : SZLData(&SZL_ID_01A0_IDX_0000,sizeof(SZL_ID_01A0_IDX_0000));break;
+ case 0x0001 : SZLData(&SZL_ID_01A0_IDX_0001,sizeof(SZL_ID_01A0_IDX_0001));break;
+ case 0x0002 : SZLData(&SZL_ID_01A0_IDX_0002,sizeof(SZL_ID_01A0_IDX_0002));break;
+ case 0x0003 : SZLData(&SZL_ID_01A0_IDX_0003,sizeof(SZL_ID_01A0_IDX_0003));break;
+ case 0x0004 : SZLData(&SZL_ID_01A0_IDX_0004,sizeof(SZL_ID_01A0_IDX_0004));break;
+ case 0x0005 : SZLData(&SZL_ID_01A0_IDX_0005,sizeof(SZL_ID_01A0_IDX_0005));break;
+ case 0x0006 : SZLData(&SZL_ID_01A0_IDX_0006,sizeof(SZL_ID_01A0_IDX_0006));break;
+ case 0x0007 : SZLData(&SZL_ID_01A0_IDX_0007,sizeof(SZL_ID_01A0_IDX_0007));break;
+ case 0x0008 : SZLData(&SZL_ID_01A0_IDX_0008,sizeof(SZL_ID_01A0_IDX_0008));break;
+ case 0x0009 : SZLData(&SZL_ID_01A0_IDX_0009,sizeof(SZL_ID_01A0_IDX_0009));break;
+ case 0x000A : SZLData(&SZL_ID_01A0_IDX_000A,sizeof(SZL_ID_01A0_IDX_000A));break;
+ case 0x000B : SZLData(&SZL_ID_01A0_IDX_000B,sizeof(SZL_ID_01A0_IDX_000B));break;
+ case 0x000C : SZLData(&SZL_ID_01A0_IDX_000C,sizeof(SZL_ID_01A0_IDX_000C));break;
+ case 0x000D : SZLData(&SZL_ID_01A0_IDX_000D,sizeof(SZL_ID_01A0_IDX_000D));break;
+ case 0x000E : SZLData(&SZL_ID_01A0_IDX_000E,sizeof(SZL_ID_01A0_IDX_000E));break;
+ case 0x000F : SZLData(&SZL_ID_01A0_IDX_000F,sizeof(SZL_ID_01A0_IDX_000F));break;
+ case 0x0010 : SZLData(&SZL_ID_01A0_IDX_0010,sizeof(SZL_ID_01A0_IDX_0010));break;
+ case 0x0011 : SZLData(&SZL_ID_01A0_IDX_0011,sizeof(SZL_ID_01A0_IDX_0011));break;
+ case 0x0012 : SZLData(&SZL_ID_01A0_IDX_0012,sizeof(SZL_ID_01A0_IDX_0012));break;
+ case 0x0013 : SZLData(&SZL_ID_01A0_IDX_0013,sizeof(SZL_ID_01A0_IDX_0013));break;
+ case 0x0014 : SZLData(&SZL_ID_01A0_IDX_0014,sizeof(SZL_ID_01A0_IDX_0014));break;
+ case 0x0015 : SZLData(&SZL_ID_01A0_IDX_0015,sizeof(SZL_ID_01A0_IDX_0015));break;
+ case 0x0174 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0174_IDX_0001,sizeof(SZL_ID_0174_IDX_0001));break;
+ case 0x0004 : SZLData(&SZL_ID_0174_IDX_0004,sizeof(SZL_ID_0174_IDX_0004));break;
+ case 0x0005 : SZLData(&SZL_ID_0174_IDX_0005,sizeof(SZL_ID_0174_IDX_0005));break;
+ case 0x0006 : SZLData(&SZL_ID_0174_IDX_0006,sizeof(SZL_ID_0174_IDX_0006));break;
+ case 0x000B : SZLData(&SZL_ID_0174_IDX_000B,sizeof(SZL_ID_0174_IDX_000B));break;
+ case 0x000C : SZLData(&SZL_ID_0174_IDX_000C,sizeof(SZL_ID_0174_IDX_000C));break;
+ case 0x0194 : switch(SZL.Index){
+ case 0x0064 : SZLData(&SZL_ID_0194_IDX_0064,sizeof(SZL_ID_0194_IDX_0064));break;
+ case 0x0694 : switch(SZL.Index){
+ case 0x0064 : SZLData(&SZL_ID_0694_IDX_0064,sizeof(SZL_ID_0694_IDX_0064));break;
+ case 0x0232 : switch(SZL.Index){
+ case 0x0001 : SZLData(&SZL_ID_0232_IDX_0001,sizeof(SZL_ID_0232_IDX_0001));break;
+ case 0x0004 : SZLData(&SZL_ID_0232_IDX_0004,sizeof(SZL_ID_0232_IDX_0004));break;
+ case 0x0C91 : switch(SZL.Index){
+ case 0x07FE : SZLData(&SZL_ID_0C91_IDX_07FE,sizeof(SZL_ID_0C91_IDX_07FE));break;
+ // Event
+ if (SZL.SZLDone)
+ DoEvent(evcReadSZL,evrNoError,SZL.ID,SZL.Index,0,0);
+ DoEvent(evcReadSZL,evrInvalidSZL,SZL.ID,SZL.Index,0,0);
+bool TS7Worker::PerformGroupSecurity()
+ PResDataSecurity ResData;
+ TS7Answer17 Answer;
+ ReqParams=PReqFunSecurity(pbyte(PDUH_in)+ReqHeaderSize);
+ ResParams=PResParamsSecurity(pbyte(&Answer)+ResHeaderSize17);
+ ResData =PResDataSecurity(pbyte(ResParams)+sizeof(TResParamsSecurity));
+ Answer.Header.PDUType=PduType_userdata;
+ Answer.Header.ParLen =SwapWord(sizeof(TResParamsSecurity));
+ Answer.Header.DataLen=SwapWord(0x0004);
+ ResParams->Head[0]=ReqParams->Head[0];
+ ResParams->Head[1]=ReqParams->Head[1];
+ ResParams->Head[2]=ReqParams->Head[2];
+ ResParams->Plen =0x08;
+ ResParams->Tg =0x85; // Type response, group functions info
+ ResParams->SubFun=ReqParams->SubFun;
+ ResParams->Seq =ReqParams->Seq;
+ ResParams->resvd =0x0000;
+ // Data
+ ResData->Ret =0x0A;
+ ResData->TS =0x00;
+ ResData->DLen=0x0000;
+ TotalSize=26;
+ isoSendBuffer(&Answer,TotalSize);
+ switch (ReqParams->SubFun)
+ case SFun_EnterPwd : DoEvent(evcSecurity,evrNoError,evsSetPassword,0,0,0); break;
+ case SFun_CancelPwd : DoEvent(evcSecurity,evrNoError,evsClrPassword,0,0,0); break;
+ default : DoEvent(evcSecurity,evrNoError,evsUnknown,0,0,0);
+bool TS7Worker::PerformGetClock()
+ PResDataGetTime Data;
+ ResParams=PS7ResParams7(pbyte(&Answer)+ResHeaderSize17);
+ Data =PResDataGetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
+ PTime =PS7Time(pbyte(Data)+6);
+ Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
+ Answer.Header.DataLen=SwapWord(sizeof(TResDataGetTime));
+ ResParams->Tg =0x87; // Type response, group functions info
+ Data->Length =SwapWord(10);
+ Data->Rsvd =0x00;
+ Data->HiYear =0x20; // Year 2000 +
+ TotalSize=36;
+ DoEvent(evcClock,evrNoError,evsGetClock,0,0,0);
+bool TS7Worker::PerformSetClock()
+ PResDataSetTime Data;
+ Data =PResDataSetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
+ Answer.Header.DataLen=SwapWord(sizeof(TResDataSetTime));
+ Data->RetVal =0x0A;
+ Data->TSize =0x00;
+ Data->Length =0x0000;
+ DoEvent(evcClock,evrNoError,evsSetClock,0,0,0);
+// S7 SERVER CLASS
+TSnap7Server::TSnap7Server()
+ CSRWHook = new TSnapCriticalSection();
+ OnReadEvent=NULL;
+ memset(&DB,0,sizeof(DB));
+ memset(&HA,0,sizeof(HA));
+ DBCount=0;
+ DBLimit=0;
+ ForcePDU = 0;
+ ResourceLess = false;
+ LocalPort=isoTcpPort;
+ CpuStatus=S7CpuStatusRun;
+ WorkInterval=100;
+TSnap7Server::~TSnap7Server()
+ DisposeAll();
+ delete CSRWHook;
+PWorkerSocket TSnap7Server::CreateWorkerSocket(socket_t Sock)
+ PWorkerSocket Result;
+ Result = new TS7Worker();
+ Result->SetSocket(Sock);
+ PS7Worker(Result)->FServer=this;
+PS7Area TSnap7Server::FindDB(word DBNumber)
+ int max=DBLimit+1;
+ for (c=0; c<max; c++)
+ if (DB[c]!=NULL)
+ if (DB[c]->Number==DBNumber)
+ return DB[c];
+int TSnap7Server::IndexOfDB(word DBNumber)
+ return c;
+int TSnap7Server::FindFirstFreeDB()
+ for (c=0; c < MaxDB; c++)
+ if (DB[c]==NULL)
+int TSnap7Server::RegisterDB(word Number, void *pUsrData, word Size)
+ PS7Area TheArea;
+ int index;
+ if (pUsrData==0)
+ return errSrvDBNullPointer;
+ if (FindDB(Number)!=NULL)
+ return errSrvAreaAlreadyExists;
+ index=FindFirstFreeDB();
+ if (index==-1)
+ return errSrvTooManyDB;
+ TheArea =new TS7Area;
+ TheArea->Number=Number;
+ TheArea->cs=new TSnapCriticalSection();
+ TheArea->PData=pbyte(pUsrData);
+ TheArea->Size=Size;
+ DB[index]=TheArea;
+ DBCount++;
+ if (DBLimit<index)
+ DBLimit=index;
+void TSnap7Server::DisposeAll()
+ PS7Area TheDB;
+ // Unregister DBs
+ for (c = 0; c < MaxDB; c++)
+ // Unregister should be done with the server in stop mode
+ // however we can minimize the risk...
+ TheDB=DB[c];
+ DB[c]=NULL;
+ if (TheDB->cs!=0)
+ delete TheDB->cs;
+ delete TheDB;
+ // Unregister other
+ for (c = srvAreaPE; c < srvAreaDB; c++)
+ UnregisterSys(c);
+int TSnap7Server::RegisterSys(int AreaCode, void *pUsrData, word Size)
+ if ((AreaCode<srvAreaPE) || (AreaCode>srvAreaTM))
+ return errSrvUnknownArea;
+ if (HA[AreaCode]==0)
+ TheArea=new TS7Area;
+ HA[AreaCode]=TheArea;
+int TSnap7Server::UnregisterDB(word DBNumber)
+ int index = IndexOfDB(DBNumber);
+ return errSrvInvalidParams;
+ TheDB=DB[index];
+ DB[index]=NULL;
+ if (TheDB->cs!=NULL)
+ DBCount--;
+int TSnap7Server::UnregisterSys(int AreaCode)
+ if (HA[AreaCode]!=NULL)
+ TheArea=HA[AreaCode];
+ HA[AreaCode]=NULL;
+ if (TheArea->cs!=NULL)
+ delete TheArea->cs;
+ delete TheArea;
+int TSnap7Server::StartTo(const char *Address)
+ return TCustomMsgServer::StartTo(Address, LocalPort);
+int TSnap7Server::GetParam(int ParamNumber, void *pValue)
+ case p_i32_MaxClients:
+ *Pint32_t(pValue)=MaxClients;
+ *Pint32_t(pValue) = ForcePDU;
+ default: return errSrvInvalidParamNumber;
+int TSnap7Server::SetParam(int ParamNumber, void *pValue)
+ if (Status == SrvStopped)
+ LocalPort = *Puint16_t(pValue);
+ return errSrvCannotChangeParam;
+ int PDU = *Pint32_t(pValue);
+ if (PDU == 0)
+ ForcePDU = 0; // ForcePDU=0 --> The server accepts the client's proposal
+ if ((PDU < MinPduSize) || (PDU>IsoPayload_Size))
+ return errSrvInvalidParams; // Wrong value
+ ForcePDU = PDU; // The server imposes ForcePDU as PDU size
+ if (ClientsCount==0 && Status==SrvStopped)
+ MaxClients=*Pint32_t(pValue);
+int TSnap7Server::RegisterArea(int AreaCode, word Index, void *pUsrData, word Size)
+ if (AreaCode==srvAreaDB)
+ return RegisterDB(Index, pUsrData, Size);
+ return RegisterSys(AreaCode,pUsrData, Size);
+int TSnap7Server::UnregisterArea(int AreaCode, word Index)
+ return UnregisterDB(Index);
+ if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
+ return UnregisterSys(AreaCode);
+int TSnap7Server::LockArea(int AreaCode, word DBNumber)
+ if (HA[AreaCode]!=0)
+ HA[AreaCode]->cs->Enter();
+ index=IndexOfDB(DBNumber);
+ if (index!=-1)
+ DB[index]->cs->Enter();
+int TSnap7Server::UnlockArea(int AreaCode, word DBNumber)
+ HA[AreaCode]->cs->Leave();
+ DB[index]->cs->Leave();
+int TSnap7Server::SetReadEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr)
+ OnReadEvent = PCallBack;
+ FReadUsrPtr = UsrPtr;
+int TSnap7Server::SetRWAreaCallBack(pfn_RWAreaCallBack PCallBack, void *UsrPtr)
+ OnRWArea = PCallBack;
+ FRWAreaUsrPtr = UsrPtr;
+ ResourceLess = OnRWArea != NULL;
+void TSnap7Server::DoReadEvent(int Sender, longword Code, word RetCode, word Param1,
+ word Param2, word Param3, word Param4)
+ TSrvEvent SrvReadEvent;
+ if (!Destroying && (OnReadEvent != NULL))
+ CSEvent->Enter();
+ time(&SrvReadEvent.EvtTime);
+ SrvReadEvent.EvtSender = Sender;
+ SrvReadEvent.EvtCode = Code;
+ SrvReadEvent.EvtRetCode = RetCode;
+ SrvReadEvent.EvtParam1 = Param1;
+ SrvReadEvent.EvtParam2 = Param2;
+ SrvReadEvent.EvtParam3 = Param3;
+ SrvReadEvent.EvtParam4 = Param4;
+ try
+ { // callback is outside here, we have to shield it
+ OnReadEvent(FReadUsrPtr, &SrvReadEvent, sizeof (TSrvEvent));
+ } catch (...)
+ CSEvent->Leave();
+bool TSnap7Server::DoReadArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData)
+ TS7Tag Tag;
+ bool Result = false;
+ if (!Destroying && (OnRWArea != NULL))
+ CSRWHook->Enter();
+ Tag.Area = Area;
+ Tag.DBNumber = DBNumber;
+ Tag.Start = Start;
+ Tag.Size = Size;
+ Tag.WordLen = WordLen;
+ // callback is outside here, we have to shield it
+ Result = OnRWArea(FRWAreaUsrPtr, Sender, OperationRead, &Tag, pUsrData) == 0;
+ catch (...)
+ Result = false;
+ CSRWHook->Leave();
+bool TSnap7Server::DoWriteArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData)
+ Result = OnRWArea(FRWAreaUsrPtr, Sender, OperationWrite, &Tag, pUsrData) == 0;
@@ -0,0 +1,261 @@
+#ifndef s7_server_h
+#define s7_server_h
+#include "snap_tcpsrvr.h"
+// Maximum number of DB, change it to increase/decrease the limit.
+// The DB table size is 12*MaxDB bytes
+#define MaxDB 2048 // Like a S7 318
+#define MinPduSize 240
+#define CPU315PduSize 240
+// Server Interface errors
+const longword errSrvDBNullPointer = 0x00200000; // Pssed null as PData
+ word Number; // Number (only for DB)
+ word Size; // Area size (in bytes)
+ pbyte PData; // Pointer to area
+ PSnapCriticalSection cs;
+}TS7Area, *PS7Area;
+// ISOTCP WORKER CLASS
+class TIsoTcpWorker : public TIsoTcpSocket
+ virtual bool IsoPerformCommand(int &Size);
+ virtual bool ExecuteSend();
+ virtual bool ExecuteRecv();
+ TIsoTcpWorker(){};
+ ~TIsoTcpWorker(){};
+ // Worker execution
+// SZL frame
+ PReqFunReadSZLFirst ReqParams;
+ PS7ReqSZLData ReqData;
+ pbyte ResData;
+ int ID;
+ int Index;
+ bool SZLDone;
+}TSZL;
+// Current Event Info
+ word EvRetCode;
+ word EvArea;
+ word EvIndex;
+ word EvStart;
+ word EvSize;
+}TEv;
+// Current Block info
+ word evError;
+}TCB;
+class TSnap7Server; // forward declaration
+class TS7Worker : public TIsoTcpWorker
+ int DBCnt;
+ byte LastBlk;
+ TSZL SZL;
+ byte BCD(word Value);
+ // Checks the consistence of the incoming PDU
+ bool CheckPDU_in(int PayloadSize);
+ void FillTime(PS7Time PTime);
+ bool ExecuteRecv();
+ void DoEvent(longword Code, word RetCode, word Param1, word Param2,
+ word Param3, word Param4);
+ void DoReadEvent(longword Code, word RetCode, word Param1, word Param2,
+ void FragmentSkipped(int Size);
+ // Entry parse
+ bool IsoPerformCommand(int &Size);
+ // First stage parse
+ bool PerformPDUAck(int &Size);
+ bool PerformPDURequest(int &Size);
+ bool PerformPDUUsrData(int &Size);
+ // Second stage parse : PDU Request
+ PS7Area GetArea(byte S7Code, word index);
+ // Group Read Area
+ bool PerformFunctionRead();
+ // Subfunctions Read Data
+ word ReadArea(PResFunReadItem ResItemData, PReqFunReadItem ReqItemPar,
+ int &PDURemainder,TEv &EV);
+ word RA_NotFound(PResFunReadItem ResItem, TEv &EV);
+ word RA_OutOfRange(PResFunReadItem ResItem, TEv &EV);
+ word RA_SizeOverPDU(PResFunReadItem ResItem, TEv &EV);
+ // Group Write Area
+ bool PerformFunctionWrite();
+ // Subfunctions Write Data
+ byte WriteArea(PReqFunWriteDataItem ReqItemData, PReqFunWriteItem ReqItemPar,
+ TEv &EV);
+ byte WA_NotFound(TEv &EV);
+ byte WA_InvalidTransportSize(TEv &EV);
+ byte WA_OutOfRange(TEv &EV);
+ byte WA_DataSizeMismatch(TEv &EV);
+ // Negotiate PDU Length
+ bool PerformFunctionControl(byte PduFun);
+ // Up/Download
+ bool PerformFunctionUpload();
+ bool PerformFunctionDownload();
+ // Second stage parse : PDU User data
+ bool PerformGroupProgrammer();
+ bool PerformGroupCyclicData();
+ bool PerformGroupSecurity();
+ // Group Block(s) Info
+ bool PerformGroupBlockInfo();
+ // Subfunctions Block info
+ void BLK_ListAll(TCB &CB);
+ void BLK_ListBoT(byte BlockType, bool Start, TCB &CB);
+ void BLK_NoResource_ListBoT(PDataFunGetBot Data, TCB &CB);
+ void BLK_GetBlkInfo(TCB &CB);
+ void BLK_NoResource_GetBlkInfo(PResDataBlockInfo Data, TCB &CB);
+ void BLK_GetBlockNum_GetBlkInfo(int &BlkNum, PReqDataBlockInfo ReqData);
+ void BLK_DoBlockInfo_GetBlkInfo(PS7Area DB, PResDataBlockInfo Data, TCB &CB);
+ // Clock Group
+ bool PerformGetClock();
+ bool PerformSetClock();
+ // SZL Group
+ bool PerformGroupSZL();
+ // Subfunctions (called by PerformGroupSZL)
+ void SZLNotAvailable();
+ void SZLSystemState();
+ void SZLData(void *P, int len);
+ void SZL_ID424();
+ void SZL_ID131_IDX003();
+ TSnap7Server *FServer;
+ int FPDULength;
+ TS7Worker();
+ ~TS7Worker(){};
+typedef TS7Worker *PS7Worker;
+extern "C"
+ typedef int (S7API *pfn_RWAreaCallBack)(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData);
+class TSnap7Server : public TCustomMsgServer
+ // Read Callback related
+ pfn_SrvCallBack OnReadEvent;
+ pfn_RWAreaCallBack OnRWArea;
+ // Critical section to lock Read/Write Hook Area
+ PSnapCriticalSection CSRWHook;
+ void *FReadUsrPtr;
+ void *FRWAreaUsrPtr;
+ void DisposeAll();
+ int FindFirstFreeDB();
+ int IndexOfDB(word DBNumber);
+ int DBLimit;
+ PS7Area DB[MaxDB]; // DB
+ PS7Area HA[5]; // MK,PE,PA,TM,CT
+ PS7Area FindDB(word DBNumber);
+ PWorkerSocket CreateWorkerSocket(socket_t Sock);
+ bool ResourceLess;
+ word ForcePDU;
+ int RegisterDB(word Number, void *pUsrData, word Size);
+ int RegisterSys(int AreaCode, void *pUsrData, word Size);
+ int UnregisterDB(word DBNumber);
+ int UnregisterSys(int AreaCode);
+ // The Read event
+ void DoReadEvent(int Sender, longword Code, word RetCode, word Param1,
+ word Param2, word Param3, word Param4);
+ bool DoReadArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData);
+ bool DoWriteArea(int Sender, int Area, int DBNumber, int Start, int Size, int WordLen, void *pUsrData);
+ int WorkInterval;
+ byte CpuStatus;
+ TSnap7Server();
+ ~TSnap7Server();
+ int LockArea(int AreaCode, word DBNumber);
+ int UnlockArea(int AreaCode, word DBNumber);
+ // Sets Event callback
+ int SetReadEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr);
+ int SetRWAreaCallBack(pfn_RWAreaCallBack PCallBack, void *UsrPtr);
+ friend class TS7Worker;
+typedef TSnap7Server *PSnap7Server;
+#endif // s7_server_h
@@ -0,0 +1,788 @@
+#include "s7_text.h"
+#ifndef OS_WINDOWS
+static char* itoa(int value, char* result, int base) {
+ // check that the base if valid
+ if (base < 2 || base > 36){
+ *result = '\0'; return result;
+ char* ptr = result, *ptr1 = result, tmp_char;
+ int tmp_value;
+ tmp_value = value;
+ value /= base;
+ *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
+ } while ( value );
+ // Apply negative sign
+ if (tmp_value < 0) *ptr++ = '-';
+ *ptr-- = '\0';
+ while(ptr1 < ptr) {
+ tmp_char = *ptr;
+ *ptr--= *ptr1;
+ *ptr1++ = tmp_char;
+ return result;
+char* NumToString(int Value, int Base, int Len, char* Result)
+ char CNumber[64];
+ char Pad[65] = "0000000000000000000000000000000000000000000000000000000000000000";
+ itoa(Value, CNumber, Base);
+ if (Len > 0)
+ int Delta = Len - strlen(CNumber); // Len is max 8 in this program
+ if (Delta > 0)
+ strncpy(Result, Pad, Delta);
+ Result[Delta] = '\0';
+ strcat(Result, CNumber);
+ strcpy(Result, CNumber);
+char* IntToString(int Value, char* Result)
+ return NumToString(Value, 10, 0, Result);
+char* TimeToString(time_t dt, char* Result)
+ struct tm * DateTime = localtime(&dt);
+ if (DateTime != NULL)
+ strftime(Result, 50, "%Y-%m-%d %H:%M:%S", DateTime);
+ *Result = '\0';
+char* IpAddressToString(int IP, char* Result)
+ in_addr Addr;
+ Addr.s_addr = IP;
+ strcpy(Result, inet_ntoa(Addr));
+#define WSAEINVALIDADDRESS 12001
+char* TcpTextOf(int Error, char* Result)
+ switch (Error)
+ case 0: *Result='\0';break;
+ case WSAEINTR: strcpy(Result," TCP : Interrupted system call\0");break;
+ case WSAEBADF: strcpy(Result," TCP : Bad file number\0");break;
+ case WSAEACCES: strcpy(Result," TCP : Permission denied\0");break;
+ case WSAEFAULT: strcpy(Result," TCP : Bad address\0");break;
+ case WSAEINVAL: strcpy(Result," TCP : Invalid argument\0");break;
+ case WSAEMFILE: strcpy(Result," TCP : Too many open files\0");break;
+ case WSAEWOULDBLOCK: strcpy(Result," TCP : Operation would block\0");break;
+ case WSAEINPROGRESS: strcpy(Result," TCP : Operation now in progress\0");break;
+ case WSAEALREADY: strcpy(Result," TCP : Operation already in progress\0");break;
+ case WSAENOTSOCK: strcpy(Result," TCP : Socket operation on non socket\0");break;
+ case WSAEDESTADDRREQ: strcpy(Result," TCP : Destination address required\0");break;
+ case WSAEMSGSIZE: strcpy(Result," TCP : Message too long\0");break;
+ case WSAEPROTOTYPE: strcpy(Result," TCP : Protocol wrong type for Socket\0");break;
+ case WSAENOPROTOOPT: strcpy(Result," TCP : Protocol not available\0");break;
+ case WSAEPROTONOSUPPORT: strcpy(Result," TCP : Protocol not supported\0");break;
+ case WSAESOCKTNOSUPPORT: strcpy(Result," TCP : Socket not supported\0");break;
+ case WSAEOPNOTSUPP: strcpy(Result," TCP : Operation not supported on Socket\0");break;
+ case WSAEPFNOSUPPORT: strcpy(Result," TCP : Protocol family not supported\0");break;
+ case WSAEAFNOSUPPORT: strcpy(Result," TCP : Address family not supported\0");break;
+ case WSAEADDRINUSE: strcpy(Result," TCP : Address already in use\0");break;
+ case WSAEADDRNOTAVAIL: strcpy(Result," TCP : Can't assign requested address\0");break;
+ case WSAENETDOWN: strcpy(Result," TCP : Network is down\0");break;
+ case WSAENETUNREACH: strcpy(Result," TCP : Network is unreachable\0");break;
+ case WSAENETRESET: strcpy(Result," TCP : Network dropped connection on reset\0");break;
+ case WSAECONNABORTED: strcpy(Result," TCP : Software caused connection abort\0");break;
+ case WSAECONNRESET: strcpy(Result," TCP : Connection reset by peer\0");break;
+ case WSAENOBUFS: strcpy(Result," TCP : No Buffer space available\0");break;
+ case WSAEISCONN: strcpy(Result," TCP : Socket is already connected\0");break;
+ case WSAENOTCONN: strcpy(Result," TCP : Socket is not connected\0");break;
+ case WSAESHUTDOWN: strcpy(Result," TCP : Can't send after Socket shutdown\0");break;
+ case WSAETOOMANYREFS: strcpy(Result," TCP : Too many references:can't splice\0");break;
+ case WSAETIMEDOUT: strcpy(Result," TCP : Connection timed out\0");break;
+ case WSAECONNREFUSED: strcpy(Result," TCP : Connection refused\0");break;
+ case WSAELOOP: strcpy(Result," TCP : Too many levels of symbolic links\0");break;
+ case WSAENAMETOOLONG: strcpy(Result," TCP : File name is too long\0");break;
+ case WSAEHOSTDOWN: strcpy(Result," TCP : Host is down\0");break;
+ case WSAEHOSTUNREACH: strcpy(Result," TCP : Unreachable peer\0");break;
+ case WSAENOTEMPTY: strcpy(Result," TCP : Directory is not empty\0");break;
+ case WSAEUSERS: strcpy(Result," TCP : Too many users\0");break;
+ case WSAEDQUOT: strcpy(Result," TCP : Disk quota exceeded\0");break;
+ case WSAESTALE: strcpy(Result," TCP : Stale NFS file handle\0");break;
+ case WSAEREMOTE: strcpy(Result," TCP : Too many levels of remote in path\0");break;
+ #ifdef OS_WINDOWS
+ case WSAEPROCLIM: strcpy(Result," TCP : Too many processes\0");break;
+ case WSASYSNOTREADY: strcpy(Result," TCP : Network subsystem is unusable\0");break;
+ case WSAVERNOTSUPPORTED: strcpy(Result," TCP : Winsock DLL cannot support this application\0");break;
+ case WSANOTINITIALISED: strcpy(Result," TCP : Winsock not initialized\0");break;
+ case WSAEDISCON: strcpy(Result," TCP : Disconnect\0");break;
+ case WSAHOST_NOT_FOUND: strcpy(Result," TCP : Host not found\0");break;
+ case WSATRY_AGAIN: strcpy(Result," TCP : Non authoritative - host not found\0");break;
+ case WSANO_RECOVERY: strcpy(Result," TCP : Non recoverable error\0");break;
+ case WSANO_DATA: strcpy(Result," TCP : Valid name, no data record of requested type\0");break;
+ case WSAEINVALIDADDRESS: strcpy(Result," TCP : Invalid address\0");break;
+ char CNumber[16];
+ strcpy(Result, " TCP : Other Socket error (");
+ strcat(Result, IntToString(Error, CNumber));
+ strcat(Result, ")");
+char* IsoTextOf(int Error, char* Result)
+ case 0 : *Result='\0';break;
+ case errIsoConnect: strcpy(Result," ISO : Connection error\0");break;
+ case errIsoDisconnect: strcpy(Result," ISO : Disconnect error\0");break;
+ case errIsoInvalidPDU: strcpy(Result," ISO : Bad PDU format\0");break;
+ case errIsoInvalidDataSize: strcpy(Result," ISO : Datasize passed to send/recv buffer is invalid\0");break;
+ case errIsoNullPointer: strcpy(Result," ISO : Null passed as pointer\0");break;
+ case errIsoShortPacket: strcpy(Result," ISO : A short packet received\0");break;
+ case errIsoTooManyFragments: strcpy(Result," ISO : Too many packets without EoT flag\0");break;
+ case errIsoPduOverflow: strcpy(Result," ISO : The sum of fragments data exceded maximum packet size\0");break;
+ case errIsoSendPacket: strcpy(Result," ISO : An error occurred during send\0");break;
+ case errIsoRecvPacket: strcpy(Result," ISO : An error occurred during recv\0");break;
+ case errIsoInvalidParams: strcpy(Result," ISO : Invalid connection params (wrong TSAPs)\0");break;
+ strcpy(Result, " ISO : Unknown error (0x");
+ strcat(Result, NumToString(Error, 16, 8, CNumber));
+char* CliTextOf(int Error, char* Result)
+ case errNegotiatingPDU : strcpy(Result,"CPU : Error in PDU negotiation\0");break;
+ case errCliInvalidParams : strcpy(Result,"CLI : invalid param(s) supplied\0");break;
+ case errCliJobPending : strcpy(Result,"CLI : Job pending\0");break;
+ case errCliTooManyItems : strcpy(Result,"CLI : too may items (>20) in multi read/write\0");break;
+ case errCliInvalidWordLen : strcpy(Result,"CLI : invalid WordLength\0");break;
+ case errCliPartialDataWritten : strcpy(Result,"CLI : Partial data written\0");break;
+ case errCliSizeOverPDU : strcpy(Result,"CPU : total data exceeds the PDU size\0");break;
+ case errCliInvalidPlcAnswer : strcpy(Result,"CLI : invalid CPU answer\0");break;
+ case errCliAddressOutOfRange : strcpy(Result,"CPU : Address out of range\0");break;
+ case errCliInvalidTransportSize : strcpy(Result,"CPU : Invalid Transport size\0");break;
+ case errCliWriteDataSizeMismatch : strcpy(Result,"CPU : Data size mismatch\0");break;
+ case errCliItemNotAvailable : strcpy(Result,"CPU : Item not available\0");break;
+ case errCliInvalidValue : strcpy(Result,"CPU : Invalid value supplied\0");break;
+ case errCliCannotStartPLC : strcpy(Result,"CPU : Cannot start PLC\0");break;
+ case errCliAlreadyRun : strcpy(Result,"CPU : PLC already RUN\0");break;
+ case errCliCannotStopPLC : strcpy(Result,"CPU : Cannot stop PLC\0");break;
+ case errCliCannotCopyRamToRom : strcpy(Result,"CPU : Cannot copy RAM to ROM\0");break;
+ case errCliCannotCompress : strcpy(Result,"CPU : Cannot compress\0");break;
+ case errCliAlreadyStop : strcpy(Result,"CPU : PLC already STOP\0");break;
+ case errCliFunNotAvailable : strcpy(Result,"CPU : Function not available\0");break;
+ case errCliUploadSequenceFailed : strcpy(Result,"CPU : Upload sequence failed\0");break;
+ case errCliInvalidDataSizeRecvd : strcpy(Result,"CLI : Invalid data size received\0");break;
+ case errCliInvalidBlockType : strcpy(Result,"CLI : Invalid block type\0");break;
+ case errCliInvalidBlockNumber : strcpy(Result,"CLI : Invalid block number\0");break;
+ case errCliInvalidBlockSize : strcpy(Result,"CLI : Invalid block size\0");break;
+ case errCliDownloadSequenceFailed : strcpy(Result,"CPU : Download sequence failed\0");break;
+ case errCliInsertRefused : strcpy(Result,"CPU : block insert refused\0");break;
+ case errCliDeleteRefused : strcpy(Result,"CPU : block delete refused\0");break;
+ case errCliNeedPassword : strcpy(Result,"CPU : Function not authorized for current protection level\0");break;
+ case errCliInvalidPassword : strcpy(Result,"CPU : Invalid password\0");break;
+ case errCliNoPasswordToSetOrClear : strcpy(Result,"CPU : No password to set or clear\0");break;
+ case errCliJobTimeout : strcpy(Result,"CLI : Job Timeout\0");break;
+ case errCliFunctionRefused : strcpy(Result,"CLI : function refused by CPU (Unknown error)\0");break;
+ case errCliPartialDataRead : strcpy(Result,"CLI : Partial data read\0");break;
+ case errCliBufferTooSmall : strcpy(Result,"CLI : The buffer supplied is too small to accomplish the operation\0");break;
+ case errCliDestroying : strcpy(Result,"CLI : Cannot perform (destroying)\0");break;
+ case errCliInvalidParamNumber : strcpy(Result,"CLI : Invalid Param Number\0");break;
+ case errCliCannotChangeParam : strcpy(Result,"CLI : Cannot change this param now\0");break;
+ strcpy(Result, "CLI : Unknown error (0x");
+char* SrvTextOf(int Error, char* Result)
+ case 0: *Result = '\0'; break;
+ case errSrvCannotStart: strcpy(Result, "SRV : Server cannot start\0"); break;
+ case errSrvDBNullPointer: strcpy(Result, "SRV : Null passed as area pointer\0"); break;
+ case errSrvAreaAlreadyExists: strcpy(Result, "SRV : Cannot register area since already exists\0"); break;
+ case errSrvUnknownArea: strcpy(Result, "SRV : Unknown Area code\0"); break;
+ case errSrvInvalidParams: strcpy(Result, "SRV : Invalid param(s) supplied\0"); break;
+ case errSrvTooManyDB: strcpy(Result, "SRV : DB Limit reached\0"); break;
+ case errSrvInvalidParamNumber: strcpy(Result, "SRV : Invalid Param Number\0"); break;
+ case errSrvCannotChangeParam: strcpy(Result, "SRV : Cannot change this param now\0");break;
+ strcpy(Result, "SRV : Unknown error (0x");
+char* ParTextOf(int Error, char* Result)
+ case errParAddressInUse : strcpy(Result, "PAR : Local address already in use");break;
+ case errParNoRoom : strcpy(Result, "PAR : No more partners available");break;
+ case errServerNoRoom : strcpy(Result, "PAR : No more servers available");break;
+ case errParInvalidParams : strcpy(Result, "PAR : Invalid parameter supplied");break;
+ case errParNotLinked : strcpy(Result, "PAR : Cannot perform, Partner not linked");break;
+ case errParBusy : strcpy(Result, "PAR : Cannot perform, Partner Busy");break;
+ case errParFrameTimeout : strcpy(Result, "PAR : Frame timeout");break;
+ case errParInvalidPDU : strcpy(Result, "PAR : Invalid PDU received");break;
+ case errParSendTimeout : strcpy(Result, "PAR : Send timeout");break;
+ case errParRecvTimeout : strcpy(Result, "PAR : Recv timeout");break;
+ case errParSendRefused : strcpy(Result, "PAR : Send refused by peer");break;
+ case errParNegotiatingPDU : strcpy(Result, "PAR : Error negotiating PDU");break;
+ case errParSendingBlock : strcpy(Result, "PAR : Error Sending Block");break;
+ case errParRecvingBlock : strcpy(Result, "PAR : Error Receiving Block");break;
+ case errParBindError : strcpy(Result, "PAR : Error Binding");break;
+ case errParDestroying : strcpy(Result, "PAR : Cannot perform (destroying)");break;
+ case errParInvalidParamNumber: strcpy(Result, "PAR : Invalid Param Number");break;
+ case errParCannotChangeParam : strcpy(Result, "PAR : Cannot change this param now");break;
+ case errParBufferTooSmall : strcpy(Result, "PAR : The buffer supplied is too small to accomplish the operation");break;
+ strcpy(Result, "PAR : Unknown error (0x");
+char* ErrCliText(int Error, char * Result, int TextLen)
+ char TcpError[128];
+ char IsoError[128];
+ char CliError[256];
+ case errLibInvalidParam : strncpy(Result,"LIB : Invalid param supplied\0",TextLen);break;
+ case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break;
+ CliTextOf(Error & ErrS7Mask, CliError);
+ strcat(CliError, IsoTextOf(Error & ErrIsoMask, IsoError));
+ strcat(CliError, TcpTextOf(Error & ErrTcpMask, TcpError));
+ strncpy(Result, CliError, TextLen);
+ strncpy(Result, "OK\0", TextLen);
+char* ErrSrvText(int Error, char* Result, int TextLen)
+ char SrvError[256];
+ case errLibInvalidParam: strncpy(Result, "LIB : Invalid param supplied\0", TextLen); break;
+ SrvTextOf(Error & ErrS7Mask, SrvError);
+ strcat(SrvError, IsoTextOf(Error & ErrIsoMask, IsoError));
+ strcat(SrvError, TcpTextOf(Error & ErrTcpMask, TcpError));
+ strncpy(Result, SrvError, TextLen);
+char* ErrParText(int Error, char* Result, int TextLen)
+ char ParError[256];
+ ParTextOf(Error & ErrS7Mask, ParError);
+ strcat(ParError, IsoTextOf(Error & ErrIsoMask, IsoError));
+ strcat(ParError, TcpTextOf(Error & ErrTcpMask, TcpError));
+ strncpy(Result, ParError, TextLen);
+// SERVER EVENTS TEXT
+char* SenderText(TSrvEvent &Event, char* Result)
+ char Buf[64];
+ char Add[16];
+ TimeToString(Event.EvtTime, Buf);
+ if (Event.EvtSender != 0)
+ strcat(Buf, " [");
+ strcat(Buf, IpAddressToString(Event.EvtSender, Add));
+ strcat(Buf, "] ");
+ strcat(Buf, " Server ");
+ strcpy(Result, Buf);
+char* TcpServerEventText(TSrvEvent &Event, char* Result)
+ char S[256];
+ char Buf[128];
+ strcpy(S, SenderText(Event, Buf));
+ switch (Event.EvtCode)
+ case evcServerStarted : strcat(S,"started");break;
+ case evcServerStopped : strcat(S,"stopped");break;
+ case evcListenerCannotStart:
+ strcat(S, "Cannot start listener - Socket Error : ");
+ strcat(S, TcpTextOf(Event.EvtRetCode,Buf));
+ case evcClientAdded : strcat(S,"Client added");break;
+ case evcClientRejected : strcat(S,"Client refused");break;
+ case evcClientNoRoom : strcat(S,"A client was refused due to maximum connections number");break;
+ case evcClientException : strcat(S,"Client exception");break;
+ case evcClientDisconnected : strcat(S,"Client disconnected by peer");break;
+ case evcClientTerminated : strcat(S,"Client terminated");break;
+ case evcClientsDropped:
+ strcat(S, IntToString(Event.EvtParam1, Buf));
+ strcat(S, " clients have been dropped bacause unresponsive");
+ strcat(S, "Unknown event (");
+ strcat(S, IntToString(Event.EvtCode, Buf));
+ strcat(S,")");
+ strcpy(Result, S);
+char* PDUText(TSrvEvent &Event, char* Result)
+ switch (Event.EvtRetCode)
+ case evrFragmentRejected:
+ strcpy(S, "Fragment of ");
+ strcat(S, " bytes rejected");
+ case evrMalformedPDU:
+ strcpy(S, "Malformed PDU of ");
+ case evrSparseBytes:
+ strcpy(S, "Message of sparse ");
+ case evrCannotHandlePDU:
+ strcpy(S, "Cannot handle this PDU");
+ case evrNotImplemented:
+ switch (Event.EvtParam1)
+ case grCyclicData:
+ strcpy(S, "Function group cyclic data not yet implemented");
+ case grProgrammer:
+ strcpy(S, "Function group programmer not yet implemented");
+ strcpy(S, "Unknown Return code (");
+ strcat(S, IntToString(Event.EvtRetCode, Buf));
+ strcat(S, ")");
+char* TxtArea(TSrvEvent &Event, char* Result)
+ char S[64];
+ char Buf[32];
+ case S7AreaPE: strcpy(S, "Area : PE, "); break;
+ case S7AreaPA: strcpy(S, "Area : PA, "); break;
+ case S7AreaMK: strcpy(S, "Area : MK, "); break;
+ case S7AreaCT: strcpy(S, "Area : CT, "); break;
+ case S7AreaTM: strcpy(S, "Area : TM, "); break;
+ case S7AreaDB:
+ strcpy(S, "Area : DB");
+ strcat(S, IntToString(Event.EvtParam2, Buf));
+ strcat(S,", ");
+ strcpy(S, "Unknown area (");
+char* TxtStartSize(TSrvEvent &Event, char* Result)
+ char N[32];
+ strcpy(Result, "Start : ");
+ strcat(Result, IntToString(Event.EvtParam3, N));
+ strcat(Result, ", Size : ");
+ strcat(Result, IntToString(Event.EvtParam4, N));
+char* TxtDataResult(TSrvEvent &Event, char* Result)
+ case evrNoError:
+ strcpy(Result," --> OK");
+ case evrErrException:
+ strcpy(Result, " --> Exception error");
+ case evrErrAreaNotFound:
+ strcpy(Result, " --> Area not found");
+ case evrErrOutOfRange:
+ strcpy(Result, " --> Out of range");
+ case evrErrOverPDU:
+ strcpy(Result, " --> Data size exceeds PDU size");
+ case evrErrTransportSize:
+ strcpy(Result, " --> Invalid transport size");
+ case evrDataSizeMismatch:
+ strcpy(Result, " --> Data size mismatch");
+ strcpy(Result, " --> Unknown error code (");
+ strcat(Result, IntToString(Event.EvtRetCode, N));
+ strcat(Result,")");
+char* ControlText(word Code, char* Result)
+ char N[64];
+ strcpy(Result, "CPU Control request : ");
+ switch (Code)
+ case CodeControlUnknown:
+ strcat(Result,"Unknown");
+ case CodeControlColdStart:
+ strcat(Result, "Cold START --> OK");
+ case CodeControlWarmStart:
+ strcat(Result, "Warm START --> OK");
+ case CodeControlStop:
+ strcat(Result, "STOP --> OK");
+ case CodeControlCompress:
+ strcat(Result, "Memory compress --> OK");
+ case CodeControlCpyRamRom:
+ strcat(Result, "Copy Ram to Rom --> OK");
+ case CodeControlInsDel:
+ strcat(Result, "Block Insert or Delete --> OK");
+ strcat(Result, "Unknown control code (");
+ strcat(Result, IntToString(Code, N));
+char* ClockText(word Code, char* Result)
+ if (Code==evsGetClock)
+ strcpy(Result,"System clock read requested");
+ strcpy(Result, "System clock write requested");
+char* ReadSZLText(TSrvEvent &Event, char* Result)
+ char S[128];
+ strcpy(S, "Read SZL request, ID:0x");
+ strcat(S, NumToString(Event.EvtParam1, 16, 4, N));
+ strcat(S, " INDEX:0x");
+ strcat(S, NumToString(Event.EvtParam2, 16, 4, N));
+ if (Event.EvtRetCode == evrNoError)
+ strcat(S, " --> OK");
+ strcat(S, " --> NOT AVAILABLE");
+char* UploadText(TSrvEvent &Event, char* Result)
+ strcpy(Result,"Block upload requested --> NOT PERFORMED (due to invalid security level)");
+char* DownloadText(TSrvEvent &Event, char* Result)
+ strcpy(Result, "Block download requested --> NOT PERFORMED (due to invalid security level)");
+char* StrBlockType(word Code, char* Result)
+ strcpy(Result, "OB");
+ strcpy(Result, "DB");
+ strcpy(Result, "SDB");
+ strcpy(Result, "FC");
+ strcpy(Result, "SFC");
+ strcpy(Result, "FB");
+ strcpy(Result, "SFB");
+ strcpy(Result, "[Unknown 0x");
+ strcat(Result, NumToString(Code, 16, 4, N));
+ strcat(Result,"]");
+char* BlockInfoText(TSrvEvent &Event, char* Result)
+ case evsGetBlockList:
+ strcpy(Result, "Block list requested");
+ case evsStartListBoT:
+ strcpy(Result, "Block of type ");
+ strcat(Result, StrBlockType(Event.EvtParam2,S));
+ strcat(Result, " list requested (start sequence)");
+ case evsListBoT:
+ strcat(Result, StrBlockType(Event.EvtParam2, S));
+ strcat(Result, " list requested (next part)");
+ case evsGetBlockInfo:
+ strcpy(Result, "Block info requested ");
+ strcat(Result, " ");
+ strcat(Result, IntToString(Event.EvtParam3,S));
+ strcat(Result, " --> OK");
+ strcat(Result, " --> NOT AVAILABLE");
+char* SecurityText(TSrvEvent &Event, char* Result)
+ case evsSetPassword:
+ strcpy(Result,"Security request : Set session password --> OK");
+ case evsClrPassword:
+ strcpy(Result, "Security request : Clear session password --> OK");
+ strcpy(Result, "Security request : Unknown Subfunction");
+char* EvtSrvText(TSrvEvent &Event, char* Result, int TextLen)
+ char C[128];
+ if (Event.EvtCode > evcSnap7Base)
+ strcpy(S, SenderText(Event, C));
+ case evcPDUincoming:
+ strcat(S, "PDU incoming : ");
+ strcat(S,PDUText(Event,C));
+ case evcDataRead:
+ strcat(S, "Read request, ");
+ strcat(S, TxtArea(Event, C));
+ strcat(S, TxtStartSize(Event, C));
+ strcat(S, TxtDataResult(Event, C));
+ case evcDataWrite:
+ strcat(S, "Write request, ");
+ case evcNegotiatePDU:
+ strcat(S, "The client requires a PDU size of ");
+ strcat(S, IntToString(Event.EvtParam1, C));
+ strcat(S," bytes");
+ case evcControl:
+ strcat(S, ControlText(Event.EvtParam1,C));
+ case evcReadSZL:
+ strcat(S, ReadSZLText(Event,C));
+ case evcClock:
+ strcat(S, ClockText(Event.EvtParam1,C));
+ case evcUpload:
+ strcat(S, UploadText(Event,C));
+ case evcDownload:
+ strcat(S, DownloadText(Event,C));
+ case evcDirectory:
+ strcat(S, BlockInfoText(Event,C));
+ case evcSecurity:
+ strcat(S, SecurityText(Event,C));
+ strcat(S, IntToString(Event.EvtCode, C));
+ strcpy(S,TcpServerEventText(Event,C));
+ strncpy(Result, S, TextLen);
@@ -0,0 +1,49 @@
+#ifndef s7_text_h
+#define s7_text_h
+// Errors areas definition
+const longword ErrTcpMask = 0x0000FFFF;
+const longword ErrIsoMask = 0x000F0000;
+const longword ErrS7Mask = 0xFFF00000;
+char* ErrCliText(int Error, char* Result, int TextLen);
+char* ErrSrvText(int Error, char* Result, int TextLen);
+char* ErrParText(int Error, char* Result, int TextLen);
+char* EvtSrvText(TSrvEvent &Event, char* Result, int TextLen);
@@ -0,0 +1,1066 @@
+#ifndef s7_types_h
+#define s7_types_h
+// EXPORT CONSTANTS
+// Everything added in this section has to be copied into wrappers interface
+#define SM7API __stdcall
+#define SM7API
+ // Area ID
+const int MaxVars = 20;
+const int S7WLChar = 0x03;
+const int S7WLInt = 0x05;
+const int S7WLDInt = 0x07;
+ // Block type
+ // Sub Block Type
+ // Block languages
+ // CPU status
+const byte S7CpuStatusUnknown = 0x00;
+const byte S7CpuStatusRun = 0x08;
+const byte S7CpuStatusStop = 0x04;
+const longword evcSnap7Base = 0x00008000;
+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;
+const word evrNoError = 0;
+ // Async mode
+const int amPolling = 0;
+const int amEvent = 1;
+const int amCallBack = 2;
+// Notes for Local/Remote Port
+// If the local port for a server and remote port for a client is != 102 they
+// will be *no more compatible with S7 IsoTCP*
+// A good reason to change them could be inside a debug session under Unix.
+// Increasing the port over 1024 avoids the need of be root.
+// Obviously you need to work with the couple Snap7Client/Snap7Server and change
+// both, or, use iptable and nat the port.
+// Bool param is passed as int32_t : 0->false, 1->true
+// String param (only set) is passed as pointer
+typedef int16_t *Pint16_t;
+typedef uint16_t *Puint16_t;
+typedef int32_t *Pint32_t;
+typedef uint32_t *Puint32_t;
+typedef int64_t *Pint64_t;
+typedef uint64_t *Puint64_t;
+typedef uintptr_t *Puintptr_t;
+//-----------------------------------------------------------------------------
+// INTERNALS CONSTANTS
+const word DBMaxName = 0xFFFF; // max number (name) of DB
+const longword errS7Mask = 0xFFF00000;
+const longword errS7Base = 0x000FFFFF;
+const longword errS7notConnected = errS7Base+0x0001; // Client not connected
+const longword errS7InvalidMode = errS7Base+0x0002; // Requested a connection to...
+const longword errS7InvalidPDUin = errS7Base+0x0003; // Malformed input PDU
+// S7 outcoming Error code
+const word Code7Ok = 0x0000;
+const word Code7AddressOutOfRange = 0x0005;
+const word Code7InvalidTransportSize = 0x0006;
+const word Code7WriteDataSizeMismatch = 0x0007;
+const word Code7ResItemNotAvailable = 0x000A;
+const word Code7ResItemNotAvailable1 = 0xD209;
+const word Code7InvalidValue = 0xDC01;
+const word Code7NeedPassword = 0xD241;
+const word Code7InvalidPassword = 0xD602;
+const word Code7NoPasswordToClear = 0xD604;
+const word Code7NoPasswordToSet = 0xD605;
+const word Code7FunNotAvailable = 0x8104;
+const word Code7DataOverPDU = 0x8500;
+// Result transport size
+const byte TS_ResBit = 0x03;
+const byte TS_ResByte = 0x04;
+const byte TS_ResInt = 0x05;
+const byte TS_ResReal = 0x07;
+const byte TS_ResOctet = 0x09;
+// Client Job status (lib internals, not S7)
+// Control codes
+const word CodeControlUnknown = 0;
+const word CodeControlColdStart = 1; // Cold start
+const word CodeControlWarmStart = 2; // Warm start
+const word CodeControlStop = 3; // Stop
+const word CodeControlCompress = 4; // Compress
+const word CodeControlCpyRamRom = 5; // Copy Ram to Rom
+const word CodeControlInsDel = 6; // Insert in working ram the block downloaded
+ // Delete from working ram the block selected
+// PDU Type
+const byte PduType_request = 1; // family request
+const byte PduType_response = 3; // family response
+const byte PduType_userdata = 7; // family user data
+// PDU Functions
+const byte pduResponse = 0x02; // Response (when error)
+const byte pduFuncRead = 0x04; // Read area
+const byte pduFuncWrite = 0x05; // Write area
+const byte pduNegotiate = 0xF0; // Negotiate PDU length
+const byte pduStart = 0x28; // CPU start
+const byte pduStop = 0x29; // CPU stop
+const byte pduStartUpload = 0x1D; // Start Upload
+const byte pduUpload = 0x1E; // Upload
+const byte pduEndUpload = 0x1F; // EndUpload
+const byte pduReqDownload = 0x1A; // Start Download request
+const byte pduDownload = 0x1B; // Download request
+const byte pduDownloadEnded = 0x1C; // Download end request
+const byte pduControl = 0x28; // Control (insert/delete..)
+// PDU SubFunctions
+const byte SFun_ListAll = 0x01; // List all blocks
+const byte SFun_ListBoT = 0x02; // List Blocks of type
+const byte SFun_BlkInfo = 0x03; // Get Block info
+const byte SFun_ReadSZL = 0x01; // Read SZL
+const byte SFun_ReadClock = 0x01; // Read Clock (Date and Time)
+const byte SFun_SetClock = 0x02; // Set Clock (Date and Time)
+const byte SFun_EnterPwd = 0x01; // Enter password for this session
+const byte SFun_CancelPwd = 0x02; // Cancel password for this session
+const byte SFun_Insert = 0x50; // Insert block
+const byte SFun_Delete = 0x42; // Delete block
+typedef tm *PTimeStruct;
+// HEADERS
+// Incoming header, it will be mapped onto IsoPDU payload
+ byte P; // Telegram ID, always 32
+ byte PDUType; // Header type 1 or 7
+ word AB_EX; // AB currently unknown, maybe it can be used for long numbers.
+ word Sequence; // Message ID. This can be used to make sure a received answer
+ word ParLen; // Length of parameters which follow this header
+ word DataLen; // Length of data which follow the parameters
+}TS7ReqHeader;
+typedef TS7ReqHeader* PS7ReqHeader;
+// Outcoming 12 bytes header , response for Request type 1
+ byte PDUType; // Header type 2 or 3
+ word Error; // Error code
+} TS7ResHeader23;
+typedef TS7ResHeader23* PS7ResHeader23;
+// Outcoming 10 bytes header , response for Request type 7
+}TS7ResHeader17;
+typedef TS7ResHeader17* PS7ResHeader17;
+// Outcoming 10 bytes header , response for Request type 8 (server control)
+ byte PDUType; // Header type 8
+ word AB_EX; // Zero
+ word DataLen; // Length of data which follow this header
+} TS7ResHeader8;
+typedef TS7ResHeader8* PS7ResHeader8;
+// Outcoming answer buffer header type 2 or header type 3
+ TS7ResHeader23 Header;
+ byte ResData [IsoPayload_Size - sizeof(TS7ResHeader23)];
+} TS7Answer23;
+typedef TS7Answer23* PS7Answer23;
+// Outcoming buffer header type 1 or header type 7
+ TS7ResHeader17 Header;
+ byte ResData [IsoPayload_Size - sizeof(TS7ResHeader17)];
+} TS7Answer17;
+typedef TS7Answer17* PS7Answer17;
+typedef byte TTimeBuffer[8];
+typedef byte *PTimeBuffer[8];
+ byte bcd_year;
+ byte bcd_mon;
+ byte bcd_day;
+ byte bcd_hour;
+ byte bcd_min;
+ byte bcd_sec;
+ byte bcd_himsec;
+ byte bcd_dow;
+}TS7Time, *PS7Time;
+typedef byte TS7Buffer[65536];
+typedef byte *PS7Buffer;
+const int ReqHeaderSize = sizeof(TS7ReqHeader);
+const int ResHeaderSize23 = sizeof(TS7ResHeader23);
+const int ResHeaderSize17 = sizeof(TS7ResHeader17);
+// Most used request type parameters record
+ byte Head[3];// 0x00 0x01 0x12
+ byte Plen; // par len 0x04
+ byte Uk; // unknown
+ byte Tg; // type and group (4 bits type and 4 bits group)
+ byte SubFun; // subfunction
+ byte Seq; // sequence
+}TReqFunTypedParams;
+ byte FunNegotiate;
+ byte Unknown;
+ word ParallelJobs_1;
+ word ParallelJobs_2;
+ word PDULength;
+}TReqFunNegotiateParams;
+typedef TReqFunNegotiateParams* PReqFunNegotiateParams;
+}TResFunNegotiateParams;
+typedef TResFunNegotiateParams* PResFunNegotiateParams;
+ byte ItemHead[3];
+ byte TransportSize;
+ word Length;
+ word DBNumber;
+ byte Area;
+ byte Address[3];
+}TReqFunReadItem, * PReqFunReadItem;
+//typedef TReqFunReadItem;
+ byte FunRead;
+ byte ItemsCount;
+ TReqFunReadItem Items[MaxVars];
+}TReqFunReadParams;
+typedef TReqFunReadParams* PReqFunReadParams;
+ byte ItemCount;
+}TResFunReadParams;
+typedef TResFunReadParams* PResFunReadParams;
+ byte ReturnCode;
+ byte Data[IsoPayload_Size - 17]; // 17 = header + params + data header - 1
+}TResFunReadItem, *PResFunReadItem;
+typedef PResFunReadItem TResFunReadData[MaxVars];
+}TReqFunWriteItem, * PReqFunWriteItem;
+ byte FunWrite;
+ TReqFunWriteItem Items[MaxVars];
+}TReqFunWriteParams;
+typedef TReqFunWriteParams* PReqFunWriteParams;
+ byte Data [IsoPayload_Size - 17]; // 17 = header + params + data header -1
+}TReqFunWriteDataItem, *PReqFunWriteDataItem;
+typedef PReqFunWriteDataItem TReqFunWriteData[MaxVars];
+ byte Data[MaxVars];
+}TResFunWrite;
+typedef TResFunWrite* PResFunWrite;
+// GROUP UPLOAD
+ byte FunSUpld; // function start upload 0x1D
+ byte Uk6 [6]; // Unknown 6 bytes
+ byte Upload_ID;
+ byte Len_1;
+ byte Prefix;
+ byte BlkPrfx; // always 0x30
+ byte BlkType;
+ byte AsciiBlk[5]; // BlockNum in ascii
+ byte A; // always 0x41 ('A')
+}TReqFunStartUploadParams;
+typedef TReqFunStartUploadParams* PReqFunStartUploadParams;
+ byte Data_1[6];
+ byte Uk[3];
+ byte LenLoad[5];
+}TResFunStartUploadParams;
+typedef TResFunStartUploadParams* PResFunStartUploadParams;
+ byte FunUpld; // function upload 0x1E
+ byte Uk6[6]; // Unknown 6 bytes
+}TReqFunUploadParams;
+typedef TReqFunUploadParams* PReqFunUploadParams;
+ byte EoU; // 0 = End Of Upload, 1 = Upload in progress
+}TResFunUploadParams;
+typedef TResFunUploadParams* PResFunUploadParams;
+ word Length; // Payload length - 4
+ byte Uk_00; // Unknown 0x00
+ byte Uk_FB; // Unknown 0xFB
+ // from here is the same of TS7CompactBlockInfo
+ word Cst_pp;
+ byte Uk_01; // Unknown 0x01
+ byte BlkFlags;
+ byte BlkLang;
+ byte SubBlkType;
+ word BlkNum;
+ u_int LenLoadMem;
+ u_int BlkSec;
+ u_int CodeTime_ms;
+ word CodeTime_dy;
+ u_int IntfTime_ms;
+ word IntfTime_dy;
+ word SbbLen;
+ word AddLen;
+ word LocDataLen;
+ word MC7Len;
+}TResFunUploadDataHeaderFirst;
+typedef TResFunUploadDataHeaderFirst* PResFunUploadDataHeaderFirst;
+ word Length;// Payload length - 4
+}TResFunUploadDataHeaderNext;
+typedef TResFunUploadDataHeaderNext* PResFunUploadDataHeaderNext;
+}TResFunUploadDataHeader;
+typedef TResFunUploadDataHeader* PResFunUploadDataHeader;
+ byte ID; // 0x65
+ word Seq; // Sequence
+ byte Const_1[10];
+ word Lo_bound;
+ word Hi_Bound;
+ byte u_shortLen;// 0x02 byte
+ // 0x04 word
+ // 0x05 int
+ // 0x06 dword
+ // 0x07 dint
+ // 0x08 real
+ byte c1, c2;
+ char Author[8];
+ char Family[8];
+ char Header[8];
+ byte B1; // 0x11
+ byte B2; // 0x00
+ word Chksum;
+ byte Uk_8[8];
+}TArrayUpldFooter;
+typedef TArrayUpldFooter* PArrayUpldFooter;
+ byte FunEUpld; // function end upload 0x1F
+}TReqFunEndUploadParams;
+typedef TReqFunEndUploadParams* PReqFunEndUploadParams;
+}TResFunEndUploadParams;
+typedef TResFunEndUploadParams* PResFunEndUploadParams;
+// GROUP DOWNLOAD
+ byte FunSDwnld; // function start Download 0x1A
+ byte Dwnld_ID;
+ byte Len_1; // 0x09
+ byte Prefix; // 0x5F
+ byte P; // 0x50 ('P')
+ byte Len_2; // 0x0D
+ byte Uk1; // 0x01
+ byte AsciiLoad[6];// load memory size (MC7 size + 92)
+ byte AsciiMC7[6]; // Block size in bytes
+}TReqStartDownloadParams;
+typedef TReqStartDownloadParams* PReqStartDownloadParams;
+typedef byte TResStartDownloadParams;
+typedef TResStartDownloadParams* PResStartDownloadParams;
+ byte Fun; // pduDownload or pduDownloadEnded
+ byte Uk7[7];
+}TReqDownloadParams;
+typedef TReqDownloadParams* PReqDownloadParams;
+ byte FunDwnld; // 0x1B
+ byte EoS; // End of sequence : 0x00 - Sequence in progress : 0x01
+}TResDownloadParams;
+typedef TResDownloadParams* PResDownloadParams;
+ word DataLen;
+ word FB_00; // 0x00 0xFB
+}TResDownloadDataHeader;
+typedef TResDownloadDataHeader* PResDownloadDataHeader;
+typedef byte TResEndDownloadParams;
+typedef TResEndDownloadParams* PResEndDownloadParams;
+}TS7CompactBlockInfo;
+typedef TS7CompactBlockInfo* PS7CompactBlockInfo;
+ byte Uk_20[20];
+ byte Author[8];
+ byte Family[8];
+ byte Header[8];
+ byte Uk_12[8];
+}TS7BlockFooter;
+typedef TS7BlockFooter* PS7BlockFooter;
+// FUNCTION INSERT/DELETE
+ byte Fun; // plc control 0x28
+ byte Uk7[7]; // unknown 7
+ word Len_1; // Length part 1 : 10
+ byte NumOfBlocks; // number of blocks to insert
+ byte ByteZero; // 0x00
+ byte AsciiZero; // 0x30 '0'
+ byte SFun; // 0x50 or 0x42
+ byte Len_2; // Length part 2 : 0x05 bytes
+ char Cmd[5]; // ascii '_INSE' or '_DELE'
+}TReqControlBlockParams;
+typedef TReqControlBlockParams* PReqControlBlockParams;
+// FUNCTIONS START/STOP/COPY RAM TO ROM/COMPRESS
+ byte Fun; // stop 0x29
+ byte Uk_5[5]; // unknown 5 bytes 0x00
+ byte Len_2; // Length part 2 : 0x09
+ char Cmd[9]; // ascii 'P_PROGRAM'
+}TReqFunPlcStop;
+typedef TReqFunPlcStop* PReqFunPlcStop;
+ byte Fun; // start 0x28
+ byte Uk_7[7]; // unknown 7
+ word Len_1; // Length part 1 : 0x0000
+ char Cmd [9]; // ascii 'P_PROGRAM'
+}TReqFunPlcHotStart;
+typedef TReqFunPlcHotStart* PReqFunPlcHotStart;
+ word Len_1; // Length part 1 : 0x0002
+ word SFun; // 'C ' 0x4320
+}TReqFunPlcColdStart;
+typedef TReqFunPlcColdStart* PReqFunPlcColdStart;
+ byte Fun; // pduControl 0x28
+ word SFun; // 'EP' 0x4550
+ byte Len_2; // Length part 2 : 0x05
+ char Cmd[5]; // ascii '_MODU'
+}TReqFunCopyRamToRom;
+typedef TReqFunCopyRamToRom* PReqFunCopyRamToRom;
+ word Len_1; // Length part 1 : 0x00
+ char Cmd[5]; // ascii '_GARB'
+}TReqFunCompress;
+typedef TReqFunCompress* PReqFunCompress;
+ byte ResFun;
+ byte para;
+}TResFunCtrl;
+typedef TResFunCtrl* PResFunCtrl;
+// FUNCTIONS USERDATA
+ byte Head[3]; // Always 0x00 0x01 0x12
+ byte Plen; // par len 0x04 or 0x08
+ word resvd; // present if plen=0x08 (S7 manager online functions)
+ word Err; // present if plen=0x08 (S7 manager online functions)
+}TS7Params7;
+typedef TS7Params7* PS7ReqParams7;
+typedef TS7Params7* PS7ResParams7;
+// for convenience Hi order bit of type are included (0x4X)
+const byte grProgrammer = 0x41;
+const byte grCyclicData = 0x42;
+const byte grBlocksInfo = 0x43;
+const byte grSZL = 0x44;
+const byte grPassword = 0x45;
+const byte grBSend = 0x46;
+const byte grClock = 0x47;
+const byte grSecurity = 0x45;
+// GROUP SECURITY
+typedef TReqFunTypedParams TReqFunSecurity;
+typedef TReqFunSecurity* PReqFunSecurity;
+typedef char TS7Password[8];
+ byte Ret; // 0xFF for request
+ byte TS; // 0x09 Transport size
+ word DLen; // Data len : 8 bytes
+ byte Pwd[8]; // Password encoded into "AG" format
+}TReqDataSecurity;
+typedef TReqDataSecurity* PReqDataSecurity;
+typedef TS7Params7 TResParamsSecurity;
+typedef TResParamsSecurity* PResParamsSecurity;
+ byte Ret;
+ byte TS;
+ word DLen;
+}TResDataSecurity;
+typedef TResDataSecurity* PResDataSecurity;
+// GROUP BLOCKS SZL
+typedef TReqFunTypedParams TReqFunReadSZLFirst;
+typedef TReqFunReadSZLFirst* PReqFunReadSZLFirst;
+ byte Head[3]; // 0x00 0x01 0x12
+ word Rsvd; // Reserved 0x0000
+ word ErrNo; // Error Code
+}TReqFunReadSZLNext;
+typedef TReqFunReadSZLNext* PReqFunReadSZLNext;
+ word DLen; // Data len
+ word ID; // SZL-ID
+ word Index;// SZL-Index
+}TS7ReqSZLData;
+typedef TS7ReqSZLData* PS7ReqSZLData;
+ word ID;
+ word Index;
+ word ListLen;
+ word ListCount;
+ word Data[32747];
+}TS7ResSZLDataFirst;
+typedef TS7ResSZLDataFirst* PS7ResSZLDataFirst;
+ word Data[32751];
+}TS7ResSZLDataNext;
+typedef TS7ResSZLDataNext* PS7ResSZLDataNext;
+ byte OtherInfo[9];
+ word Count;
+ word Items[32747];
+}TS7ResSZLData_0;
+typedef TS7ResSZLData_0* PS7ResSZLData_0;
+// GROUP CLOCK
+typedef TReqFunTypedParams TReqFunDateTime;
+typedef TReqFunDateTime* PReqFunDateTime;
+typedef byte TReqDataGetDateTime[4];
+typedef longword *PReqDataGetDateTime;
+ byte RetVal;
+ byte TSize;
+ byte Rsvd;
+ byte HiYear;
+ TTimeBuffer Time;
+}TResDataGetTime;
+typedef TResDataGetTime* PResDataGetTime;
+typedef TResDataGetTime TReqDataSetTime;
+typedef TReqDataSetTime* PReqDataSetTime;
+}TResDataSetTime;
+typedef TResDataSetTime* PResDataSetTime;
+// GROUP BLOCKS INFO
+typedef TReqFunTypedParams TReqFunGetBlockInfo;
+typedef TReqFunGetBlockInfo* PReqFunGetBlockInfo;
+typedef byte TReqDataFunBlocks[4];
+typedef u_char* PReqDataFunBlocks;
+}TResFunGetBlockInfo;
+typedef TResFunGetBlockInfo* PResFunGetBlockInfo;
+ byte Zero; // always 0x30 -> Ascii 0
+ byte BType; // Block Type
+ word BCount; // Block count
+}TResFunGetBlockItem;
+ byte TRSize;
+ TResFunGetBlockItem Blocks[7];
+}TDataFunListAll;
+typedef TDataFunListAll* PDataFunListAll;
+ word BlockNum;
+ byte BlockLang;
+}TDataFunGetBotItem;
+ TDataFunGetBotItem Items[(IsoPayload_Size - 29 ) / 4];
+}TDataFunGetBot;
+// Note : 29 is the size of headers iso, COPT, S7 header, params, data
+typedef TDataFunGetBot* PDataFunGetBot;
+ byte RetVal; // 0xFF
+ byte TSize; // Octet (0x09)
+ word Length; // 0x0002
+ byte Zero; // Ascii '0' (0x30)
+}TReqDataBlockOfType;
+typedef TReqDataBlockOfType* PReqDataBlockOfType;
+}TReqDataBlockInfo;
+typedef TReqDataBlockInfo* PReqDataBlockInfo;
+ byte Cst_b;
+ word Cst_w1;
+ word Cst_w2;
+ byte Unknown_1;
+ word BlkNumber;
+ byte BlkSec[4];
+ byte Version;
+ byte Unknown_2;
+ word BlkChksum;
+ byte Resvd1[4];
+ byte Resvd2[4];
+}TResDataBlockInfo;
+typedef TResDataBlockInfo* PResDataBlockInfo;
+// BSEND / BRECV
+ byte Data[65536];
+}TPendingBuffer;
+ TTPKT TPKT;
+ TCOTP_DT COTP;
+ byte P;
+}TPacketInfo;
+ byte Head[3];// Always 0x00 0x01 0x12
+ byte Uk; // unknown (0x12)
+ byte Tg; // type and group, 4 bits type and 4 bits group (0x46)
+ byte SubFun; // subfunction (0x01)
+ byte IDSeq; // ID Sequence (come from partner)
+ byte EoS; // End of Sequence = 0x00 Sequence in progress = 0x01;
+ word Err; //
+}TBSendParams;
+typedef TBSendParams* PBSendReqParams;
+typedef TBSendParams* PBSendResParams;
+// Data frame
+ byte FF; // 0xFF
+ byte TRSize; // Transport Size 0x09 (octet)
+ word Len; // This Telegram Length
+ byte DHead[4];// sequence 0x12 0x06 0x13 0x00
+ u_int R_ID; // R_ID
+}TBsendRequestData;
+typedef TBsendRequestData* PBsendRequestData;
+ byte DHead[4]; // sequence 0x0A 0x00 0x00 0x00
+}TBSendResData;
+typedef TBSendResData* PBSendResData;
+#endif // s7_types_h
@@ -0,0 +1,129 @@
+LIBRARY SNAP7.DLL
+EXPORTS
+ Cli_Create
+ Cli_Destroy
+ Cli_ConnectTo
+ Cli_SetConnectionParams
+ Cli_SetConnectionType
+ Cli_Connect
+ Cli_Disconnect
+ Cli_GetParam
+ Cli_SetParam
+ Cli_SetAsCallback
+ Cli_ReadArea
+ Cli_WriteArea
+ Cli_ReadMultiVars
+ Cli_WriteMultiVars
+ Cli_DBRead
+ Cli_DBWrite
+ Cli_MBRead
+ Cli_MBWrite
+ Cli_EBRead
+ Cli_EBWrite
+ Cli_ABRead
+ Cli_ABWrite
+ Cli_TMRead
+ Cli_TMWrite
+ Cli_CTRead
+ Cli_CTWrite
+ Cli_ListBlocks
+ Cli_GetAgBlockInfo
+ Cli_GetPgBlockInfo
+ Cli_ListBlocksOfType
+ Cli_Upload
+ Cli_FullUpload
+ Cli_Download
+ Cli_Delete
+ Cli_DBGet
+ Cli_DBFill
+ Cli_GetPlcDateTime
+ Cli_SetPlcDateTime
+ Cli_SetPlcSystemDateTime
+ Cli_GetOrderCode
+ Cli_GetCpuInfo
+ Cli_GetCpInfo
+ Cli_ReadSZL
+ Cli_ReadSZLList
+ Cli_PlcHotStart
+ Cli_PlcColdStart
+ Cli_PlcStop
+ Cli_CopyRamToRom
+ Cli_Compress
+ Cli_GetPlcStatus
+ Cli_GetProtection
+ Cli_SetSessionPassword
+ Cli_ClearSessionPassword
+ Cli_IsoExchangeBuffer
+ Cli_GetExecTime
+ Cli_GetLastError
+ Cli_GetPduLength
+ Cli_AsReadArea
+ Cli_AsWriteArea
+ Cli_AsDBRead
+ Cli_AsDBWrite
+ Cli_AsMBRead
+ Cli_AsMBWrite
+ Cli_AsEBRead
+ Cli_AsEBWrite
+ Cli_AsABRead
+ Cli_AsABWrite
+ Cli_AsTMRead
+ Cli_AsTMWrite
+ Cli_AsCTRead
+ Cli_AsCTWrite
+ Cli_AsListBlocksOfType
+ Cli_AsReadSZL
+ Cli_AsReadSZLList
+ Cli_AsUpload
+ Cli_AsFullUpload
+ Cli_AsDownload
+ Cli_AsCopyRamToRom
+ Cli_AsCompress
+ Cli_AsDBGet
+ Cli_AsDBFill
+ Cli_CheckAsCompletion
+ Cli_WaitAsCompletion
+ Cli_ErrorText
+ Cli_GetConnected
+ Srv_Create
+ Srv_Destroy
+ Srv_GetParam
+ Srv_SetParam
+ Srv_StartTo
+ Srv_Start
+ Srv_Stop
+ Srv_RegisterArea
+ Srv_UnregisterArea
+ Srv_LockArea
+ Srv_UnlockArea
+ Srv_GetStatus
+ Srv_SetCpuStatus
+ Srv_ClearEvents
+ Srv_PickEvent
+ Srv_GetMask
+ Srv_SetMask
+ Srv_SetEventsCallback
+ Srv_SetReadEventsCallback
+ Srv_SetRWAreaCallback
+ Srv_ErrorText
+ Srv_EventText
+ Par_Create
+ Par_Destroy
+ Par_GetParam
+ Par_SetParam
+ Par_StartTo
+ Par_Start
+ Par_Stop
+ Par_BSend
+ Par_AsBSend
+ Par_CheckAsBSendCompletion
+ Par_WaitAsBSendCompletion
+ Par_SetSendCallback
+ Par_BRecv
+ Par_CheckAsBRecvCompletion
+ Par_SetRecvCallback
+ Par_GetTimes
+ Par_GetStats
+ Par_GetLastError
+ Par_GetStatus
+ Par_ErrorText
@@ -0,0 +1,1196 @@
+| PROJECT SNAP7 1.4.1 |
+#include "snap7_libmain.h"
+void libinit(void) __attribute__((constructor));
+void libdone(void) __attribute__((destructor));
+static bool libresult = true;
+void libinit(void)
+ // in future expansions here can be inserted some initialization code
+ libresult=true;
+void libdone(void)
+ // in future expansions here can be inserted some destruction code
+BOOL APIENTRY DllMain (HINSTANCE hInst,
+ DWORD reason,
+ LPVOID reserved)
+ switch (reason)
+ case DLL_PROCESS_ATTACH:
+ libinit();
+ case DLL_PROCESS_DETACH:
+ libdone();
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ return libresult;
+//***************************************************************************
+S7Object S7API Cli_Create()
+ return S7Object(new TSnap7Client());
+void S7API Cli_Destroy(S7Object &Client)
+ if (Client)
+ delete PSnap7Client(Client);
+ Client=0;
+int S7API Cli_SetConnectionParams(S7Object Client, const char *Address, word LocalTSAP, word RemoteTSAP)
+ PSnap7Client(Client)->SetConnectionParams(Address, LocalTSAP, RemoteTSAP);
+ return errLibInvalidObject;
+int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType)
+ PSnap7Client(Client)->SetConnectionType(ConnectionType);
+int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot)
+ return PSnap7Client(Client)->ConnectTo(Address, Rack, Slot);
+int S7API Cli_Connect(S7Object Client)
+ return PSnap7Client(Client)->Connect();
+int S7API Cli_Disconnect(S7Object Client)
+ return PSnap7Client(Client)->Disconnect();
+int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue)
+ return PSnap7Client(Client)->GetParam(ParamNumber, pValue);
+int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue)
+ return PSnap7Client(Client)->SetParam(ParamNumber, pValue);
+int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr)
+ return PSnap7Client(Client)->SetAsCallback(pCompletion, usrPtr);
+int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return PSnap7Client(Client)->ReadArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return PSnap7Client(Client)->WriteArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount)
+ return PSnap7Client(Client)->ReadMultiVars(Item, ItemsCount);
+int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount)
+ return PSnap7Client(Client)->WriteMultiVars(Item, ItemsCount);
+int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->DBRead(DBNumber, Start, Size, pUsrData);
+int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->DBWrite(DBNumber, Start, Size, pUsrData);
+int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->MBRead(Start, Size, pUsrData);
+int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->MBWrite(Start, Size, pUsrData);
+int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->EBRead(Start, Size, pUsrData);
+int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->EBWrite(Start, Size, pUsrData);
+int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->ABRead(Start, Size, pUsrData);
+int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->ABWrite(Start, Size, pUsrData);
+int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->TMRead(Start, Amount, pUsrData);
+int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->TMWrite(Start, Amount, pUsrData);
+int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->CTRead(Start, Amount, pUsrData);
+int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->CTWrite(Start, Amount, pUsrData);
+int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData)
+ return PSnap7Client(Client)->ListBlocks(pUsrData);
+int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData)
+ return PSnap7Client(Client)->GetAgBlockInfo(BlockType, BlockNum, pUsrData);
+int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size)
+ return PSnap7Client(Client)->GetPgBlockInfo(pBlock, pUsrData, Size);
+int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount)
+ return PSnap7Client(Client)->ListBlocksOfType(BlockType, pUsrData, ItemsCount);
+int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->Upload(BlockType, BlockNum, pUsrData, Size);
+int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->FullUpload(BlockType, BlockNum, pUsrData, Size);
+int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size)
+ return PSnap7Client(Client)->Download(BlockNum, pUsrData, Size);
+int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum)
+ return PSnap7Client(Client)->Delete(BlockType, BlockNum);
+int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->DBGet(DBNumber, pUsrData, Size);
+int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar)
+ return PSnap7Client(Client)->DBFill(DBNumber, FillChar);
+int S7API Cli_GetPlcDateTime(S7Object Client, tm &DateTime)
+ return PSnap7Client(Client)->GetPlcDateTime(DateTime);
+int S7API Cli_SetPlcDateTime(S7Object Client, tm *DateTime)
+ return PSnap7Client(Client)->SetPlcDateTime(DateTime);
+int S7API Cli_SetPlcSystemDateTime(S7Object Client)
+ return PSnap7Client(Client)->SetPlcSystemDateTime();
+int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData)
+ return PSnap7Client(Client)->GetOrderCode(pUsrData);
+int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData)
+ return PSnap7Client(Client)->GetCpuInfo(pUsrData);
+int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData)
+ return PSnap7Client(Client)->GetCpInfo(pUsrData);
+int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size)
+ return PSnap7Client(Client)->ReadSZL(ID, Index, pUsrData, Size);
+int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount)
+ return PSnap7Client(Client)->ReadSZLList(pUsrData, ItemsCount);
+int S7API Cli_PlcHotStart(S7Object Client)
+ return PSnap7Client(Client)->PlcHotStart();
+int S7API Cli_PlcColdStart(S7Object Client)
+ return PSnap7Client(Client)->PlcColdStart();
+int S7API Cli_PlcStop(S7Object Client)
+ return PSnap7Client(Client)->PlcStop();
+int S7API Cli_CopyRamToRom(S7Object Client, int Timeout)
+ return PSnap7Client(Client)->CopyRamToRom(Timeout);
+int S7API Cli_Compress(S7Object Client, int Timeout)
+ return PSnap7Client(Client)->Compress(Timeout);
+int S7API Cli_GetPlcStatus(S7Object Client, int &Status)
+ return PSnap7Client(Client)->GetPlcStatus(Status);
+int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData)
+ return PSnap7Client(Client)->GetProtection(pUsrData);
+int S7API Cli_SetSessionPassword(S7Object Client, char *Password)
+ return PSnap7Client(Client)->SetSessionPassword(Password);
+int S7API Cli_ClearSessionPassword(S7Object Client)
+ return PSnap7Client(Client)->ClearSessionPassword();
+int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->isoExchangeBuffer(pUsrData, Size);
+int S7API Cli_GetExecTime(S7Object Client, int &Time)
+ Time=PSnap7Client(Client)->Time();
+int S7API Cli_GetLastError(S7Object Client, int &LastError)
+ LastError=PSnap7Client(Client)->LastError;
+int S7API Cli_GetPduLength(S7Object Client, int &Requested, int &Negotiated)
+ Negotiated=PSnap7Client(Client)->PDULength;
+ Requested =PSnap7Client(Client)->PDURequest;
+int S7API Cli_ErrorText(int Error, char *Text, int TextLen)
+ ErrCliText(Error, Text, TextLen);
+ Text[TextLen-1] = '\0';
+ return errLibInvalidParam;
+int S7API Cli_GetConnected(S7Object Client, int &Connected)
+ Connected=0;
+ Connected=PSnap7Client(Client)->Connected;
+int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return PSnap7Client(Client)->AsReadArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+ return PSnap7Client(Client)->AsWriteArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsDBRead(DBNumber, Start, Size, pUsrData);
+int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsDBWrite(DBNumber, Start, Size, pUsrData);
+int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsMBRead(Start, Size, pUsrData);
+int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsMBWrite(Start, Size, pUsrData);
+int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsEBRead(Start, Size, pUsrData);
+int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsEBWrite(Start, Size, pUsrData);
+int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsABRead(Start, Size, pUsrData);
+int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData)
+ return PSnap7Client(Client)->AsABWrite(Start, Size, pUsrData);
+int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->AsTMRead(Start, Amount, pUsrData);
+int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->AsTMWrite(Start, Amount, pUsrData);
+int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->AsCTRead(Start, Amount, pUsrData);
+int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+ return PSnap7Client(Client)->AsCTWrite(Start, Amount, pUsrData);
+int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount)
+ return PSnap7Client(Client)->AsListBlocksOfType(BlockType, pUsrData, ItemsCount);
+int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size)
+ return PSnap7Client(Client)->AsReadSZL(ID, Index, pUsrData, Size);
+int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount)
+ return PSnap7Client(Client)->AsReadSZLList(pUsrData, ItemsCount);
+int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->AsUpload(BlockType, BlockNum, pUsrData, Size);
+int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->AsFullUpload(BlockType, BlockNum, pUsrData, Size);
+int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size)
+ return PSnap7Client(Client)->AsDownload(BlockNum, pUsrData, Size);
+int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout)
+ return PSnap7Client(Client)->AsCopyRamToRom(Timeout);
+int S7API Cli_AsCompress(S7Object Client, int Timeout)
+int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size)
+ return PSnap7Client(Client)->AsDBGet(DBNumber, pUsrData, Size);
+int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar)
+ return PSnap7Client(Client)->AsDBFill(DBNumber, FillChar);
+int S7API Cli_CheckAsCompletion(S7Object Client, int &opResult)
+ if (PSnap7Client(Client)->CheckAsCompletion(opResult))
+ return JobComplete;
+ return JobPending;
+int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout)
+ return PSnap7Client(Client)->WaitAsCompletion(Timeout);
+S7Object S7API Srv_Create()
+ return S7Object(new TSnap7Server());
+void S7API Srv_Destroy(S7Object &Server)
+ if (Server)
+ delete PSnap7Server(Server);
+int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue)
+ return PSnap7Server(Server)->GetParam(ParamNumber, pValue);
+int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue)
+ return PSnap7Server(Server)->SetParam(ParamNumber, pValue);
+int S7API Srv_StartTo(S7Object Server, const char *Address)
+ return PSnap7Server(Server)->StartTo(Address);
+int S7API Srv_Start(S7Object Server)
+ return PSnap7Server(Server)->Start();
+int S7API Srv_Stop(S7Object Server)
+ PSnap7Server(Server)->Stop();
+int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size)
+ return PSnap7Server(Server)->RegisterArea(AreaCode, Index, pUsrData, Size);
+int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index)
+ return PSnap7Server(Server)->UnregisterArea(AreaCode, Index);
+int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index)
+ return PSnap7Server(Server)->LockArea(AreaCode, Index);
+int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index)
+ return PSnap7Server(Server)->UnlockArea(AreaCode, Index);
+int S7API Srv_GetStatus(S7Object Server, int &ServerStatus, int &CpuStatus, int &ClientsCount)
+ ServerStatus=PSnap7Server(Server)->Status;
+ CpuStatus=PSnap7Server(Server)->CpuStatus;
+ ClientsCount=PSnap7Server(Server)->ClientsCount;
+int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus)
+ PSnap7Server(Server)->CpuStatus=CpuStatus;
+int S7API Srv_ErrorText(int Error, char *Text, int TextLen)
+ ErrSrvText(Error, Text, TextLen);
+int S7API Srv_EventText(TSrvEvent &Event, char *Text, int TextLen)
+ EvtSrvText(Event, Text, TextLen);
+ //Text[TextLen] = '\0';
+int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int &EvtReady)
+ EvtReady=0;
+ EvtReady=int(PSnap7Server(Server)->PickEvent(pEvent));
+int S7API Srv_ClearEvents(S7Object Server)
+ PSnap7Server(Server)->EventsFlush();
+int S7API Srv_GetMask(S7Object Server, int MaskKind, longword &Mask)
+ Mask=0;
+ if ((MaskKind==mkEvent) || (MaskKind==mkLog))
+ if (MaskKind==mkEvent)
+ Mask=PSnap7Server(Server)->EventMask;
+ Mask=PSnap7Server(Server)->LogMask;
+int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask)
+ PSnap7Server(Server)->EventMask=Mask;
+ PSnap7Server(Server)->LogMask=Mask;
+int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr)
+ return PSnap7Server(Server)->SetEventsCallBack(pCallback, usrPtr);
+int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr)
+ return PSnap7Server(Server)->SetReadEventsCallBack(pCallback, usrPtr);
+int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr)
+ return PSnap7Server(Server)->SetRWAreaCallBack(pCallback, usrPtr);
+S7Object S7API Par_Create(int Active)
+ return S7Object(new TSnap7Partner(Active!=0));
+void S7API Par_Destroy(S7Object &Partner)
+ if (Partner)
+ delete PSnap7Partner(Partner);
+ Partner=0;
+int S7API Par_GetParam(S7Object Partner, int ParamNumber, void *pValue)
+ return PSnap7Partner(Partner)->GetParam(ParamNumber, pValue);
+int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue)
+ return PSnap7Partner(Partner)->SetParam(ParamNumber, pValue);
+ word LocTsap, word RemTsap)
+ return PSnap7Partner(Partner)->StartTo(LocalAddress, RemoteAddress, LocTsap, RemTsap);
+int S7API Par_Start(S7Object Partner)
+ return PSnap7Partner(Partner)->Start();
+int S7API Par_Stop(S7Object Partner)
+ return PSnap7Partner(Partner)->Stop();
+int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size)
+ return PSnap7Partner(Partner)->BSend(R_ID, pUsrData, Size);
+int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size)
+ return PSnap7Partner(Partner)->AsBSend(R_ID, pUsrData, Size);
+int S7API Par_CheckAsBSendCompletion(S7Object Partner, int &opResult)
+ if (PSnap7Partner(Partner)->CheckAsBSendCompletion(opResult))
+int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout)
+ return PSnap7Partner(Partner)->WaitAsBSendCompletion(Timeout);
+int S7API Par_SetSendCallback(S7Object Partner, pfn_ParBSendCompletion pCompletion, void *usrPtr)
+ return PSnap7Partner(Partner)->SetSendCallback(pCompletion, usrPtr);
+int S7API Par_BRecv(S7Object Partner, longword &R_ID, void *pData, int &Size, longword Timeout)
+ return PSnap7Partner(Partner)->BRecv(R_ID, pData, Size, Timeout);
+int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int &opResult, longword &R_ID,
+ if (PSnap7Partner(Partner)->CheckAsBRecvCompletion(opResult, R_ID, pData, Size))
+int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParBRecvCallBack pCompletion, void *usrPtr)
+ return PSnap7Partner(Partner)->SetRecvCallback(pCompletion, usrPtr);
+int S7API Par_GetTimes(S7Object Partner, longword &SendTime, longword &RecvTime)
+ SendTime=PSnap7Partner(Partner)->SendTime;
+ RecvTime=PSnap7Partner(Partner)->RecvTime;
+int S7API Par_GetStats(S7Object Partner, longword &BytesSent, longword &BytesRecv,
+ longword &SendErrors, longword &RecvErrors)
+ BytesSent=PSnap7Partner(Partner)->BytesSent;
+ BytesRecv=PSnap7Partner(Partner)->BytesRecv;
+ SendErrors=PSnap7Partner(Partner)->SendErrors;
+ RecvErrors=PSnap7Partner(Partner)->RecvErrors;
+int S7API Par_GetLastError(S7Object Partner, int &LastError)
+ LastError=PSnap7Partner(Partner)->LastError;
+int S7API Par_GetStatus(S7Object Partner, int &Status)
+ Status=PSnap7Partner(Partner)->Status();
+int S7API Par_ErrorText(int Error, char *Text, int TextLen)
+ ErrParText(Error, Text, TextLen);
+ Text[TextLen - 1] = '\0';
@@ -0,0 +1,201 @@
+#ifndef snap7_libmain_h
+#define snap7_libmain_h
+// CLIENT EXPORT LIST - Sync functions
+EXPORTSPEC S7Object S7API Cli_Create();
+EXPORTSPEC void S7API Cli_Destroy(S7Object &Client);
+EXPORTSPEC int S7API Cli_Connect(S7Object Client);
+EXPORTSPEC int S7API Cli_SetConnectionParams(S7Object Client, const char *Address, word LocalTSAP, word RemoteTSAP);
+EXPORTSPEC int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType);
+EXPORTSPEC int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot);
+EXPORTSPEC int S7API Cli_Disconnect(S7Object Client);
+EXPORTSPEC int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr);
+EXPORTSPEC int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+EXPORTSPEC int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+EXPORTSPEC int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
+EXPORTSPEC int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount);
+EXPORTSPEC int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData);
+EXPORTSPEC int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData);
+EXPORTSPEC int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size);
+EXPORTSPEC int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount);
+EXPORTSPEC int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size);
+EXPORTSPEC int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum);
+EXPORTSPEC int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar);
+EXPORTSPEC int S7API Cli_GetPlcDateTime(S7Object Client, tm &DateTime);
+EXPORTSPEC int S7API Cli_SetPlcDateTime(S7Object Client, tm *DateTime);
+EXPORTSPEC int S7API Cli_SetPlcSystemDateTime(S7Object Client);
+EXPORTSPEC int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData);
+EXPORTSPEC int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData);
+EXPORTSPEC int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData);
+EXPORTSPEC int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount);
+EXPORTSPEC int S7API Cli_PlcHotStart(S7Object Client);
+EXPORTSPEC int S7API Cli_PlcColdStart(S7Object Client);
+EXPORTSPEC int S7API Cli_PlcStop(S7Object Client);
+EXPORTSPEC int S7API Cli_CopyRamToRom(S7Object Client, int Timeout);
+EXPORTSPEC int S7API Cli_Compress(S7Object Client, int Timeout);
+EXPORTSPEC int S7API Cli_GetPlcStatus(S7Object Client, int &Status);
+EXPORTSPEC int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData);
+EXPORTSPEC int S7API Cli_SetSessionPassword(S7Object Client, char *Password);
+EXPORTSPEC int S7API Cli_ClearSessionPassword(S7Object Client);
+EXPORTSPEC int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_GetExecTime(S7Object Client, int &Time);
+EXPORTSPEC int S7API Cli_GetLastError(S7Object Client, int &LastError);
+EXPORTSPEC int S7API Cli_GetPduLength(S7Object Client, int &Requested, int &Negotiated);
+EXPORTSPEC int S7API Cli_ErrorText(int Error, char *Text, int TextLen);
+EXPORTSPEC int S7API Cli_GetConnected(S7Object Client, int &Connected);
+// CLIENT EXPORT LIST - Async functions
+EXPORTSPEC int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData);
+EXPORTSPEC int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount);
+EXPORTSPEC int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount);
+EXPORTSPEC int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size);
+EXPORTSPEC int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout);
+EXPORTSPEC int S7API Cli_AsCompress(S7Object Client, int Timeout);
+EXPORTSPEC int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size);
+EXPORTSPEC int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar);
+EXPORTSPEC int S7API Cli_CheckAsCompletion(S7Object Client, int &opResult);
+EXPORTSPEC int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout);
+// SERVER EXPORT LIST
+EXPORTSPEC S7Object S7API Srv_Create();
+EXPORTSPEC void S7API Srv_Destroy(S7Object &Server);
+EXPORTSPEC int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Srv_Start(S7Object Server);
+EXPORTSPEC int S7API Srv_StartTo(S7Object Server, const char *Address);
+EXPORTSPEC int S7API Srv_Stop(S7Object Server);
+// Data
+EXPORTSPEC int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size);
+EXPORTSPEC int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index);
+EXPORTSPEC int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index);
+EXPORTSPEC int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index);
+// Events
+EXPORTSPEC int S7API Srv_ClearEvents(S7Object Server);
+EXPORTSPEC int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int &EvtReady);
+EXPORTSPEC int S7API Srv_GetMask(S7Object Server, int MaskKind, longword &Mask);
+EXPORTSPEC int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask);
+EXPORTSPEC int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
+EXPORTSPEC int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr);
+EXPORTSPEC int S7API Srv_EventText(TSrvEvent &Event, char *Text, int TextLen);
+EXPORTSPEC int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr);
+EXPORTSPEC int S7API Srv_GetStatus(S7Object Server, int &ServerStatus, int &CpuStatus, int &ClientsCount);
+EXPORTSPEC int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus);
+EXPORTSPEC int S7API Srv_ErrorText(int Error, char *Text, int TextLen);
+// PARTNER EXPORT LIST
+EXPORTSPEC S7Object S7API Par_Create(int Active);
+EXPORTSPEC void S7API Par_Destroy(S7Object &Partner);
+EXPORTSPEC int S7API Par_GetParam(S7Object Partner, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue);
+EXPORTSPEC int S7API Par_Start(S7Object Partner);
+EXPORTSPEC int S7API Par_StartTo(S7Object Partner, const char *LocalAddress, const char *RemoteAddress,
+EXPORTSPEC int S7API Par_Stop(S7Object Partner);
+EXPORTSPEC int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
+EXPORTSPEC int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size);
+EXPORTSPEC int S7API Par_CheckAsBSendCompletion(S7Object Partner, int &opResult);
+EXPORTSPEC int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout);
+EXPORTSPEC int S7API Par_SetSendCallback(S7Object Partner, pfn_ParBSendCompletion pCompletion, void *usrPtr);
+EXPORTSPEC int S7API Par_BRecv(S7Object Partner, longword &R_ID, void *pData, int &Size, longword Timeout);
+EXPORTSPEC int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int &opResult, longword &R_ID,
+EXPORTSPEC int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParBRecvCallBack pCompletion, void *usrPtr);
+EXPORTSPEC int S7API Par_GetTimes(S7Object Partner, longword &SendTime, longword &RecvTime);
+EXPORTSPEC int S7API Par_GetStats(S7Object Partner, longword &BytesSent, longword &BytesRecv,
+ longword &SendErrors, longword &RecvErrors);
+EXPORTSPEC int S7API Par_GetLastError(S7Object Partner, int &LastError);
+EXPORTSPEC int S7API Par_GetStatus(S7Object Partner, int &Status);
+EXPORTSPEC int S7API Par_ErrorText(int Error, char *Text, int TextLen);
+#endif // snap7_libmain_h
@@ -0,0 +1,923 @@
+static SocketsLayer SocketsLayerInitializer;
+// Base class endian aware
+TSnapBase::TSnapBase()
+ int x = 1;
+ LittleEndian=*(char *)&x == 1;
+word TSnapBase::SwapWord(word Value)
+ if (LittleEndian)
+ return ((Value >> 8) & 0xFF) | ((Value << 8) & 0xFF00);
+ return Value;
+longword TSnapBase::SwapDWord(longword Value)
+ return (Value >> 24) | ((Value << 8) & 0x00FF0000) | ((Value >> 8) & 0x0000FF00) | (Value << 24);
+void Msg_CloseSocket(socket_t FSocket)
+ closesocket(FSocket);
+ close(FSocket);
+longword Msg_GetSockAddr(socket_t FSocket)
+ sockaddr_in RemoteSin;
+ int namelen = sizeof(RemoteSin);
+ uint32_t namelen = sizeof(RemoteSin);
+ namelen=sizeof(sockaddr_in);
+ if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
+ return RemoteSin.sin_addr.s_addr;
+TMsgSocket::TMsgSocket()
+ Pinger = new TPinger();
+ // Set Defaults
+ strcpy(LocalAddress,"0.0.0.0");
+ LocalPort=0;
+ strcpy(RemoteAddress,"127.0.0.1");
+ RemotePort=0;
+ RecvTimeout=500;
+ SendTimeout=10;
+ PingTimeout=750;
+ Connected=false;
+ FSocket=INVALID_SOCKET;
+ LocalBind=0;
+TMsgSocket::~TMsgSocket()
+ DestroySocket();
+ delete Pinger;
+void TMsgSocket::SetSin(sockaddr_in &sin, char *Address, u_short Port)
+ uint32_t in_addr;
+ in_addr=inet_addr(Address);
+ memset(&sin, 0, sizeof(sin));
+ if (in_addr!=INADDR_NONE)
+ sin.sin_addr.s_addr = in_addr;
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(Port);
+ LastTcpError=WSAEINVALIDADDRESS;
+void TMsgSocket::GetSin(sockaddr_in sin, char *Address, u_short &Port)
+ strcpy(Address,inet_ntoa(sin.sin_addr));
+ Port=htons(sin.sin_port);
+void TMsgSocket::GetLocal()
+ int namelen = sizeof(LocalSin);
+ uint32_t namelen = sizeof(LocalSin);
+ if (getsockname(FSocket, (struct sockaddr*)&LocalSin, &namelen)==0)
+ GetSin(LocalSin, LocalAddress, LocalPort);
+void TMsgSocket::GetRemote()
+ GetSin(RemoteSin, RemoteAddress, RemotePort);
+int TMsgSocket::GetLastSocketError()
+ return WSAGetLastError();
+ return errno;
+void TMsgSocket::Purge()
+ // small buffer to empty the socket
+ char Trash[512];
+ int Read;
+ if (CanRead(0)) {
+ Read=recv(FSocket, Trash, 512, MSG_NOSIGNAL );
+ } while(Read==512);
+void TMsgSocket::CreateSocket()
+ FSocket =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
+ if (FSocket!=INVALID_SOCKET)
+ SetSocketOptions();
+ LastTcpError =GetLastSocketError();
+void TMsgSocket::GotSocket()
+ ClientHandle=RemoteSin.sin_addr.s_addr;
+ // could be inherited it if wee need further actions on the socket
+void TMsgSocket::SetSocket(socket_t s)
+ FSocket=s;
+ GetLocal();
+ GetRemote();
+ GotSocket();
+ Connected=FSocket!=INVALID_SOCKET;
+void TMsgSocket::DestroySocket()
+ if(FSocket != INVALID_SOCKET)
+ if (shutdown(FSocket, SD_SEND)==0)
+int TMsgSocket::WaitingData()
+ int result = 0;
+ u_long x = 0;
+ if (ioctlsocket(FSocket, FIONREAD, &x) == 0)
+ result = x;
+ if (ioctl(FSocket, FIONREAD, &x) == 0)
+ if (result>MaxPacketSize)
+ result = MaxPacketSize;
+int TMsgSocket::WaitForData(int Size, int Timeout)
+ // Check for connection active
+ if (CanRead(0) && (WaitingData()==0))
+ LastTcpError=WSAECONNRESET;
+ // Enter main loop
+ Elapsed =SysGetTick();
+ while((WaitingData()<Size) && (LastTcpError==0))
+ // Checks timeout
+ if (DeltaTime(Elapsed)>=(longword)(Timeout))
+ LastTcpError =WSAETIMEDOUT;
+ SysSleep(1);
+ if(LastTcpError==WSAECONNRESET)
+ Connected =false;
+ return LastTcpError;
+void TMsgSocket::SetSocketOptions()
+ int NoDelay = 1;
+ int KeepAlive = 1;
+ SockCheck(setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&NoDelay, sizeof(NoDelay)));
+ SockCheck(setsockopt(FSocket, SOL_SOCKET, SO_KEEPALIVE,(char*)&KeepAlive, sizeof(KeepAlive)));
+int TMsgSocket::SockCheck(int SockResult)
+ if (SockResult == (int)(SOCKET_ERROR))
+ LastTcpError = GetLastSocketError();
+bool TMsgSocket::CanWrite(int Timeout)
+ timeval TimeV;
+ int64_t x;
+ fd_set FDset;
+ if(FSocket == INVALID_SOCKET)
+ TimeV.tv_usec = (Timeout % 1000) * 1000;
+ TimeV.tv_sec = Timeout / 1000;
+ FD_ZERO(&FDset);
+ FD_SET(FSocket, &FDset);
+ x = select(FSocket + 1, NULL, &FDset, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
+ if (x==(int)SOCKET_ERROR)
+ x=0;
+ return (x > 0);
+bool TMsgSocket::CanRead(int Timeout)
+ x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
+#ifdef NON_BLOCKING_CONNECT
+// Non blocking connection (UNIX) Thanks to Rolf Stalder
+int TMsgSocket::SckConnect()
+ int n, flags, err;
+ socklen_t len;
+ fd_set rset, wset;
+ struct timeval tval;
+ SetSin(RemoteSin, RemoteAddress, RemotePort);
+ if (LastTcpError == 0) {
+ CreateSocket();
+ flags = fcntl(FSocket, F_GETFL, 0);
+ if (flags >= 0) {
+ if (fcntl(FSocket, F_SETFL, flags | O_NONBLOCK) != -1) {
+ n = connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
+ if (n < 0) {
+ if (errno != EINPROGRESS) {
+ // still connecting ...
+ FD_ZERO(&rset);
+ FD_SET(FSocket, &rset);
+ wset = rset;
+ tval.tv_sec = PingTimeout / 1000;
+ tval.tv_usec = (PingTimeout % 1000) * 1000;
+ n = select(FSocket+1, &rset, &wset, NULL,
+ (PingTimeout ? &tval : NULL));
+ if (n == 0) {
+ // timeout
+ LastTcpError = WSAEHOSTUNREACH;
+ if (FD_ISSET(FSocket, &rset) || FD_ISSET(FSocket, &wset)) {
+ err = 0;
+ len = sizeof(err);
+ if (getsockopt(
+ FSocket, SOL_SOCKET, SO_ERROR, &err, &len) == 0) {
+ if (err) {
+ LastTcpError = err;
+ if (fcntl(FSocket, F_SETFL, flags) != -1) {
+ ClientHandle = LocalSin.sin_addr.s_addr;
+ LastTcpError = -1;
+ } // still connecting
+ else if (n == 0) {
+ // connected immediatly
+ } // fcntl(F_SETFL)
+ } // fcntl(F_GETFL)
+ } //valid socket
+ } // LastTcpError==0
+ Connected=LastTcpError==0;
+// Regular connection (Windows)
+ if (Ping(RemoteSin))
+ Result=connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
+ if (SockCheck(Result)==0)
+ // Client handle is self_address (here the connection is ACTIVE)
+ ClientHandle=LocalSin.sin_addr.s_addr;
+ LastTcpError=WSAEHOSTUNREACH;
+void TMsgSocket::SckDisconnect()
+void TMsgSocket::ForceClose()
+ } catch (...) {
+int TMsgSocket::SckBind()
+ int Res;
+ int Opt=1;
+ SetSin(LocalSin, LocalAddress, LocalPort);
+ setsockopt(FSocket ,SOL_SOCKET, SO_REUSEADDR, (const char *)&Opt, sizeof(int));
+ Res =bind(FSocket, (struct sockaddr*)&LocalSin, sizeof(sockaddr_in));
+ SockCheck(Res);
+ if (Res==0)
+ LocalBind=LocalSin.sin_addr.s_addr;
+int TMsgSocket::SckListen()
+ SockCheck(listen(FSocket ,SOMAXCONN));
+bool TMsgSocket::Ping(char *Host)
+ return Pinger->Ping(Host, PingTimeout);
+bool TMsgSocket::Ping(sockaddr_in Addr)
+ if (PingTimeout == 0)
+ return Pinger->Ping(Addr.sin_addr.s_addr, PingTimeout);
+socket_t TMsgSocket::SckAccept()
+ socket_t result;
+ result = accept(FSocket, NULL, NULL);
+ if(result==INVALID_SOCKET)
+int TMsgSocket::SendPacket(void *Data, int Size)
+ if (SendTimeout>0)
+ if (!CanWrite(SendTimeout))
+ LastTcpError = WSAETIMEDOUT;
+ if (send(FSocket, (char*)Data, Size, MSG_NOSIGNAL)==Size)
+ Result =SOCKET_ERROR;
+ SockCheck(Result);
+bool TMsgSocket::PacketReady(int Size)
+ return (WaitingData()>=Size);
+int TMsgSocket::Receive(void *Data, int BufSize, int &SizeRecvd)
+ if (CanRead(RecvTimeout))
+ SizeRecvd=recv(FSocket ,(char*)Data ,BufSize ,MSG_NOSIGNAL );
+ if (SizeRecvd>0) // something read (default case)
+ if (SizeRecvd==0)
+ LastTcpError = WSAECONNRESET; // Connection reset by Peer
+ LastTcpError=GetLastSocketError(); // we need to know what happened
+ Connected = false;
+int TMsgSocket::RecvPacket(void *Data, int Size)
+ int BytesRead;
+ WaitForData(Size, RecvTimeout);
+ BytesRead=recv(FSocket, (char*)Data, Size, MSG_NOSIGNAL);
+ if (BytesRead==0)
+ if (BytesRead<0)
+ else // After the timeout the bytes waiting were less then we expected
+ if (LastTcpError==WSAETIMEDOUT)
+int TMsgSocket::PeekPacket(void *Data, int Size)
+ BytesRead=recv(FSocket, (char*)Data, Size, MSG_PEEK | MSG_NOSIGNAL );
+bool TMsgSocket::Execute()
+// PING
+static int PingKind;
+// iphlpapi, is loaded dinamically because if this fails we can still try
+// to use raw sockets
+static char const *iphlpapi = "\\iphlpapi.dll";
+//typedef byte TTxBuffer[40];
+typedef byte TTxBuffer[32];
+typedef HANDLE (__stdcall *pfn_IcmpCreateFile)();
+typedef bool (__stdcall *pfn_IcmpCloseHandle)(HANDLE PingHandle);
+typedef int (__stdcall *pfn_IcmpSendEcho2)(
+ HANDLE PingHandle,
+ void *Event,
+ void *AcpRoutine,
+ void *AcpContext,
+ unsigned long DestinationAddress,
+ void *RequestData,
+ int RequestSize,
+ void *not_used, //should be *IP_OPTION_INFORMATION but we don't use it
+ void *ReplyBuffer,
+ int ReplySize,
+ int Timeout
+);
+static pfn_IcmpCreateFile IcmpCreateFile;
+static pfn_IcmpCloseHandle IcmpCloseHandle;
+static pfn_IcmpSendEcho2 IcmpSendEcho2;
+static HINSTANCE IcmpDllHandle = 0;
+static bool IcmpAvail = false;
+bool IcmpInit()
+ char iphlppath[MAX_PATH+12];
+ int PathLen = GetSystemDirectoryA(iphlppath, MAX_PATH);
+ if (PathLen != 0)
+ strcat(iphlppath, iphlpapi);
+ IcmpDllHandle = LoadLibraryA(iphlppath);
+ IcmpDllHandle = 0;
+ if (IcmpDllHandle != 0)
+ IcmpCreateFile=(pfn_IcmpCreateFile)GetProcAddress(IcmpDllHandle,"IcmpCreateFile");
+ IcmpCloseHandle=(pfn_IcmpCloseHandle)GetProcAddress(IcmpDllHandle,"IcmpCloseHandle");
+ IcmpSendEcho2=(pfn_IcmpSendEcho2)GetProcAddress(IcmpDllHandle,"IcmpSendEcho2");
+ return (IcmpCreateFile!=NULL) && (IcmpCloseHandle!=NULL) && (IcmpSendEcho2!=NULL);
+void IcmpDone()
+ if (IcmpDllHandle!=0)
+ FreeLibrary(IcmpDllHandle);
+ IcmpAvail=false;
+// RAW Socket Pinger
+TRawSocketPinger::TRawSocketPinger()
+ FSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ FId =word(size_t(this));
+ FSeq =0;
+TRawSocketPinger::~TRawSocketPinger()
+void TRawSocketPinger::InitPacket()
+ memset(&IcmpBuffer,0,ICmpBufferSize);
+ FSeq++;
+ SendPacket=PIcmpPacket(pbyte(&IcmpBuffer)+sizeof(TIPHeader));
+ SendPacket->Header.ic_type=ICMP_ECHORQ;
+ SendPacket->Header.ic_code=0;
+ SendPacket->Header.ic_cksum=0;
+ SendPacket->Header.ic_id=FId;
+ SendPacket->Header.ic_seq=FSeq;
+ memset(&SendPacket->Data,0,sizeof(SendPacket->Data));
+ SendPacket->Header.ic_cksum=PacketChecksum();
+word TRawSocketPinger::PacketChecksum()
+ word *P = (word*)(SendPacket);
+ longword Sum = 0;
+ for (c = 0; c < int(sizeof(TIcmpPacket) / 2); c++) {
+ Sum+=*P;
+ P++;
+ Sum=(Sum >> 16) + (Sum & 0xFFFF);
+ Sum=Sum+(Sum >> 16);
+ return word(~Sum);
+bool TRawSocketPinger::CanRead(int Timeout)
+ if (x==(int)(SOCKET_ERROR))
+bool TRawSocketPinger::Ping(longword ip_addr, int Timeout)
+ sockaddr_in LSockAddr;
+ sockaddr_in RSockAddr;
+ PIcmpReply Reply;
+ if (FSocket==INVALID_SOCKET)
+ // Init packet
+ InitPacket();
+ Reply=PIcmpReply(&IcmpBuffer);
+ // Init Remote and Local Addresses struct
+ RSockAddr.sin_family=AF_INET;
+ RSockAddr.sin_port=0;
+ RSockAddr.sin_addr.s_addr=ip_addr;
+ LSockAddr.sin_family=AF_INET;
+ LSockAddr.sin_port=0;
+ LSockAddr.sin_addr.s_addr=inet_addr("0.0.0.0");
+ // Bind to local
+ if (bind(FSocket, (struct sockaddr*)&LSockAddr, sizeof(sockaddr_in))!=0)
+ // Connect to remote (not a really TCP connection, only to setup the socket)
+ if (connect(FSocket, (struct sockaddr*)&RSockAddr, sizeof(sockaddr_in))!=0)
+ // Send ICMP packet
+ if (send(FSocket, (char*)SendPacket, sizeof(TIcmpPacket), MSG_NOSIGNAL)!=int(sizeof(TIcmpPacket)))
+ // Wait for a reply
+ if (!CanRead(Timeout))
+ return false;// time expired
+ // Get the answer
+ if (recv(FSocket, (char*)&IcmpBuffer, ICmpBufferSize, MSG_NOSIGNAL)<int(sizeof(TIcmpReply)))
+ // Check the answer
+ return (Reply->IPH.ip_src==RSockAddr.sin_addr.s_addr) && // the peer is what we are looking for
+ (Reply->ICmpReply.Header.ic_type==ICMP_ECHORP); // type = reply
+// Pinger
+TPinger::TPinger()
+TPinger::~TPinger()
+bool TPinger::RawPing(longword ip_addr, int Timeout)
+ PRawSocketPinger RawPinger = new TRawSocketPinger();
+ Result=RawPinger->Ping(ip_addr, Timeout);
+ delete RawPinger;
+bool TPinger::WinPing(longword ip_addr, int Timeout)
+ HANDLE PingHandle;
+ TTxBuffer TxBuffer;
+ TIcmpBuffer IcmpBuffer;
+ PingHandle = IcmpCreateFile();
+ if (PingHandle != INVALID_HANDLE_VALUE)
+ memset(&TxBuffer,'\55',sizeof(TTxBuffer));
+ Result=(IcmpSendEcho2(PingHandle, NULL, NULL, NULL, ip_addr,
+ &TxBuffer, sizeof(TxBuffer), NULL, &IcmpBuffer, ICmpBufferSize, Timeout))>0;
+ IcmpCloseHandle(PingHandle);
+bool TPinger::Ping(char *Host, int Timeout)
+ longword Addr;
+ Addr=inet_addr(Host);
+ return Ping(Addr, Timeout);
+bool TPinger::Ping(longword ip_addr, int Timeout)
+ if (PingKind==pkWinHelper)
+ return WinPing(ip_addr, Timeout);
+ if (PingKind==pkRawSocket)
+ return RawPing(ip_addr, Timeout);
+ return true; // we still need to continue
+// Checks if raw sockets are allowed
+bool RawSocketsCheck()
+ socket_t RawSocket;
+ RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ Result=RawSocket != INVALID_SOCKET;
+ closesocket(RawSocket);
+ close(RawSocket);
+// Sockets init
+// - Winsock Startup (Windows)
+// - ICMP Helper Load (Windows)
+// - Check for raw socket (Unix or Windows if ICMP load failed)
+SocketsLayer::SocketsLayer()
+ timeBeginPeriod(1); // it's not strictly related to socket but here is a nice place
+ WSAStartup(0x202,&wsaData);
+ if (IcmpInit())
+ PingKind=pkWinHelper;
+ if (RawSocketsCheck())
+ PingKind=pkRawSocket;
+ PingKind=pkCannotPing;
+SocketsLayer::~SocketsLayer()
+ IcmpDone();
+ WSACleanup();
+ timeEndPeriod(1);
@@ -0,0 +1,339 @@
+#ifndef snap_msgsock_h
+#define snap_msgsock_h
+#include "snap_sysutils.h"
+//----------------------------------------------------------------------------
+#if defined(OS_WINDOWS) || defined (OS_SOLARIS) || defined(OS_OSX)
+# define MSG_NOSIGNAL 0
+// Non blocking connection to avoid root priviledges under UNIX
+// i.e. raw socket pinger is not more used.
+// Thanks to Rolf Stalder that made it ;)
+#ifdef PLATFORM_UNIX
+ #define NON_BLOCKING_CONNECT
+ #include <fcntl.h>
+ In Windows sizeof socket varies depending of the platform :
+ win32 -> sizeof(SOCKET) = 4
+ win64 -> sizeof(SOCKET) = 8
+ Even though sizeof(SOCKET) is 8, should be safe to cast it to int, because
+ the value constitutes an index in per-process table of limited size
+ and not a real pointer.
+ Other Os define the socket as int regardless of the processor.
+ We want to sleep peacefully, so it's better to define a portable socket.
+typedef SOCKET socket_t;
+typedef int socket_t;
+#define SD_RECEIVE 0x00
+#define SD_SEND 0x01
+#define SD_BOTH 0x02
+#define MaxPacketSize 65536
+// For other platform we need to re-define next constants
+#define INVALID_SOCKET (socket_t)(~0)
+#define SOCKET_ERROR (-1)
+#define WSAEINTR EINTR
+#define WSAEBADF EBADF
+#define WSAEACCES EACCES
+#define WSAEFAULT EFAULT
+#define WSAEINVAL EINVAL
+#define WSAEMFILE EMFILE
+#define WSAEWOULDBLOCK EWOULDBLOCK
+#define WSAEINPROGRESS EINPROGRESS
+#define WSAEALREADY EALREADY
+#define WSAENOTSOCK ENOTSOCK
+#define WSAEDESTADDRREQ EDESTADDRREQ
+#define WSAEMSGSIZE EMSGSIZE
+#define WSAEPROTOTYPE EPROTOTYPE
+#define WSAENOPROTOOPT ENOPROTOOPT
+#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
+#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
+#define WSAEOPNOTSUPP EOPNOTSUPP
+#define WSAEPFNOSUPPORT EPFNOSUPPORT
+#define WSAEAFNOSUPPORT EAFNOSUPPORT
+#define WSAEADDRINUSE EADDRINUSE
+#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
+#define WSAENETDOWN ENETDOWN
+#define WSAENETUNREACH ENETUNREACH
+#define WSAENETRESET ENETRESET
+#define WSAECONNABORTED ECONNABORTED
+#define WSAECONNRESET ECONNRESET
+#define WSAENOBUFS ENOBUFS
+#define WSAEISCONN EISCONN
+#define WSAENOTCONN ENOTCONN
+#define WSAESHUTDOWN ESHUTDOWN
+#define WSAETOOMANYREFS ETOOMANYREFS
+#define WSAETIMEDOUT ETIMEDOUT
+#define WSAECONNREFUSED ECONNREFUSED
+#define WSAELOOP ELOOP
+#define WSAENAMETOOLONG ENAMETOOLONG
+#define WSAEHOSTDOWN EHOSTDOWN
+#define WSAEHOSTUNREACH EHOSTUNREACH
+#define WSAENOTEMPTY ENOTEMPTY
+#define WSAEUSERS EUSERS
+#define WSAEDQUOT EDQUOT
+#define WSAESTALE ESTALE
+#define WSAEREMOTE EREMOTE
+#define ICmpBufferSize 4096
+typedef byte TIcmpBuffer[ICmpBufferSize];
+// Ping result
+#define PR_CANNOT_PERFORM -1 // cannot ping :
+ // unix : no root rights or SUID flag set to
+ // open raw sockets
+ // windows : neither helper DLL found nor raw
+ // sockets can be opened (no administrator rights)
+ // In this case the execution continues whitout
+ // the benefit of the smart-connect.
+#define PR_SUCCESS 0 // Host found
+#define PR_ERROR 1 // Ping Error, Ping was performed but ...
+ // - host didn't replied (not found)
+ // - routing error
+ // - TTL expired
+ // - ... all other icmp error that we don't need
+ // to know.
+// Ping Kind
+#define pkCannotPing 1 // see PR_CANNOT_PERFORM comments
+#define pkWinHelper 2 // use iphlpapi.dll (only windows)
+#define pkRawSocket 3 // use raw sockets (unix/windows)
+const byte ICMP_ECHORP = 0; // ECHO Reply
+const byte ICMP_ECHORQ = 8; // ECHO Request
+// RAW SOCKET PING STRUCTS
+ byte ip_hl_v;
+ byte ip_tos;
+ word ip_len;
+ word ip_id ;
+ word ip_off;
+ byte ip_ttl;
+ byte ip_p;
+ word ip_sum;
+ longword ip_src;
+ longword ip_dst;
+}TIPHeader;
+ byte ic_type; // Type of message
+ byte ic_code; // Code
+ word ic_cksum; // 16 bit checksum
+ word ic_id; // ID (ic1 : ipv4)
+ word ic_seq; // Sequence
+}TIcmpHeader;
+ TIcmpHeader Header;
+ byte Data[32]; // use the well known default
+}TIcmpPacket, *PIcmpPacket;
+ TIPHeader IPH;
+ TIcmpPacket ICmpReply;
+}TIcmpReply, *PIcmpReply;
+class TRawSocketPinger
+ socket_t FSocket;
+ PIcmpPacket SendPacket;
+ word FId, FSeq;
+ void InitPacket();
+ word PacketChecksum();
+ bool CanRead(int Timeout);
+ bool Ping(longword ip_addr, int Timeout);
+ TRawSocketPinger();
+ ~TRawSocketPinger();
+typedef TRawSocketPinger *PRawSocketPinger;
+class TPinger
+ PRawSocketPinger RawPinger;
+ bool RawAvail;
+ bool WinPing(longword ip_addr, int Timeout);
+ bool RawPing(longword ip_addr, int Timeout);
+ TPinger();
+ ~TPinger();
+ bool Ping(char *Host, int Timeout);
+typedef TPinger *PPinger;
+class TSnapBase // base class endian-aware
+ bool LittleEndian;
+ longword SwapDWord(longword Value);
+ word SwapWord(word Value);
+ TSnapBase();
+class TMsgSocket : public TSnapBase
+ PPinger Pinger;
+ int GetLastSocketError();
+ int SockCheck(int SockResult);
+ void DestroySocket();
+ void SetSocketOptions();
+ bool CanWrite(int Timeout);
+ void GetLocal();
+ void GetRemote();
+ void SetSin(sockaddr_in &sin, char *Address, u_short Port);
+ void GetSin(sockaddr_in sin, char *Address, u_short &Port);
+ sockaddr_in LocalSin;
+ // low level socket
+ void CreateSocket();
+ // Called when a socket is assigned externally
+ void GotSocket();
+ // Returns how many bytes are ready to be read in the winsock buffer
+ int WaitingData();
+ // Waits until there at least "size" bytes ready to be read or until receive timeout occurs
+ int WaitForData(int Size, int Timeout);
+ // Clear socket input buffer
+ void Purge();
+ longword ClientHandle;
+ // Coordinates Address:Port
+ char LocalAddress[16];
+ char RemoteAddress[16];
+ word LocalPort;
+ word RemotePort;
+ // "speed" of the socket listener (used server-side)
+ // Timeouts : 3 different values for fine tuning.
+ // Send timeout should be small since with work with small packets and TCP_NO_DELAY
+ // option, so we don't expect "time to wait".
+ // Recv timeout depends of equipment's processing time : we send a packet, the equipment
+ // processes the message, finally it sends the answer. In any case Recv timeout > Send Timeout.
+ // PingTimeout is the maximum time interval during which we expect that the PLC answers.
+ // By default is 750 ms, increase it if there are many switch/repeaters.
+ int PingTimeout;
+ int RecvTimeout;
+ int SendTimeout;
+ //int ConnTimeout;
+ // Output : Last operation error
+ int LastTcpError;
+ // Output : Connected to the remote Host/Peer/Client
+ bool Connected;
+ TMsgSocket();
+ virtual ~TMsgSocket();
+ // Returns true if "something" can be read during the Timeout interval..
+ // Connects to a peer (using RemoteAddress and RemotePort)
+ int SckConnect(); // (client-side)
+ // Disconnects from a peer (gracefully)
+ void SckDisconnect();
+ // Disconnects RAW
+ void ForceClose();
+ // Binds to a local adapter (using LocalAddress and LocalPort) (server-side)
+ int SckBind();
+ // Listens for an incoming connection (server-side)
+ int SckListen();
+ // Set an external socket reference (tipically from a listener)
+ void SetSocket(socket_t s);
+ // Accepts an incoming connection returning a socket descriptor (server-side)
+ socket_t SckAccept();
+ // Pings the peer before connecting
+ bool Ping(char *Host);
+ bool Ping(sockaddr_in Addr);
+ // Sends a packet
+ int SendPacket(void *Data, int Size);
+ // Returns true if a Packet at least of "Size" bytes is ready to be read
+ bool PacketReady(int Size);
+ // Receives everything
+ int Receive(void *Data, int BufSize, int & SizeRecvd);
+ // Receives a packet of size specified.
+ int RecvPacket(void *Data, int Size);
+ // Peeks a packet of size specified without extract it from the socket queue
+ int PeekPacket(void *Data, int Size);
+ virtual bool Execute();
+typedef TMsgSocket *PMsgSocket;
+void Msg_CloseSocket(socket_t FSocket);
+longword Msg_GetSockAddr(socket_t FSocket);
+class SocketsLayer
+ WSADATA wsaData;
+ SocketsLayer();
+ ~SocketsLayer();
+#endif // snap_msgsock_h
@@ -0,0 +1,152 @@
+#ifndef snap_platform_h
+#define snap_platform_h
+#if defined (_WIN32)|| defined(_WIN64)|| defined(__WIN32__) || defined(__WINDOWS__)
+#if defined (_WIN32) && !defined(_WIN64) && !defined(_EMBEDDING_VS2013UP)
+ # define _USE_32BIT_TIME_T
+// Linux, BSD and Solaris define "unix", OSX doesn't, even though it derives from BSD
+#if __APPLE__
+// Thanks to Rolf Stalder now it's possible to use pthreads also for Solaris
+// In any case the Solaris native threads model is still present and can be
+// used uncommenting the #define line below.
+# undef OS_SOLARIS_NATIVE_THREADS
+// # define OS_SOLARIS_NATIVE_THREADS
+#if defined(PLATFORM_UNIX)
+# include <sys/param.h>
+#if (!defined (OS_WINDOWS)) && (!defined(PLATFORM_UNIX)) && (!defined(OS_BSD)) && (!defined(OS_OSX))
+# error platform still unsupported (please add it yourself and report ;-)
+# include <winsock2.h>
+# include <mmsystem.h>
+# include <sys/filio.h>
+# include <cstdlib>
+# include <string.h>
+# include <errno.h>
+# include <sys/time.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <netinet/tcp.h>
+# include <netinet/in.h>
+# include <sys/ioctl.h>
+# define EXPORTSPEC extern "C" __declspec ( dllexport )
+# define EXPORTSPEC extern "C"
+// We absolute need of them, all structs have an exact size that
+// must be the same across the processor used 32/64 bit
+// *Use them* if you change/expand the code and avoid long, u_long and so on...
+typedef uintptr_t snap_obj; // multi platform/processor object reference
+# define INFINITE 0XFFFFFFFF
+#endif // snap_platform_h
@@ -0,0 +1,73 @@
+int clock_gettime(int clk_id, struct timespec* t)
+ struct timeval now;
+ int rv = gettimeofday(&now, NULL);
+ if (rv) return rv;
+ t->tv_sec = now.tv_sec;
+ t->tv_nsec = now.tv_usec * 1000;
+longword SysGetTick()
+ return timeGetTime();
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (longword) (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+longword DeltaTime(longword &Elapsed)
+ // Checks for rollover
+ if (TheTime<Elapsed)
+ Elapsed=0;
+ return TheTime-Elapsed;
@@ -0,0 +1,39 @@
+#ifndef snap_sysutils_h
+#define snap_sysutils_h
+# define CLOCK_MONOTONIC 0
+longword SysGetTick();
+void SysSleep(longword Delay_ms);
+longword DeltaTime(longword &Elapsed);
+#endif // snap_sysutils_h
@@ -0,0 +1,487 @@
+// EVENTS QUEUE
+TMsgEventQueue::TMsgEventQueue(const int Capacity, const int BlockSize)
+ FCapacity = Capacity;
+ Max = FCapacity - 1;
+ FBlockSize = BlockSize;
+ Buffer = new byte[FCapacity * FBlockSize];
+ Flush();
+TMsgEventQueue::~TMsgEventQueue()
+ delete[] Buffer;
+void TMsgEventQueue::Flush()
+ IndexIn = 0;
+ IndexOut = 0;
+void TMsgEventQueue::Insert(void *lpdata)
+ pbyte PBlock;
+ if (!Full())
+ // Calc offset
+ if (IndexIn < Max) IndexIn++;
+ else IndexIn = 0;
+ PBlock = Buffer + uintptr_t(IndexIn * FBlockSize);
+ memcpy(PBlock, lpdata, FBlockSize);
+bool TMsgEventQueue::Extract(void *lpdata)
+ int IdxOut;
+ if (!Empty())
+ // stores IndexOut
+ IdxOut = IndexOut;
+ if (IdxOut < Max) IdxOut++;
+ else IdxOut = 0;
+ PBlock = Buffer + uintptr_t(IdxOut * FBlockSize);
+ // moves data
+ memcpy(lpdata, PBlock, FBlockSize);
+ // Updates IndexOut
+ IndexOut = IdxOut;
+bool TMsgEventQueue::Empty()
+ return (IndexIn == IndexOut);
+bool TMsgEventQueue::Full()
+ int IdxOut = IndexOut; // To avoid troubles if IndexOut changes during next line
+ return ( (IdxOut == IndexIn + 1) || ((IndexIn == Max) && (IdxOut == 0)));
+// WORKER THREAD
+TMsgWorkerThread::TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server)
+ FreeOnTerminate = true;
+ WorkerSocket = Socket;
+ FServer = Server;
+void TMsgWorkerThread::Execute()
+ bool Exception = false;
+ bool SelfClose = false;
+ // Working loop
+ while (!Terminated && !SelfClose && !Exception && !FServer->Destroying)
+ if (!WorkerSocket->Execute()) // False -> End of Activities
+ SelfClose = true;
+ Exception = true;
+ if (!FServer->Destroying)
+ // Exception detected during Worker activity
+ if (Exception)
+ WorkerSocket->ForceClose();
+ FServer->DoEvent(WorkerSocket->ClientHandle, evcClientException, 0, 0, 0, 0, 0);
+ if (SelfClose)
+ FServer->DoEvent(WorkerSocket->ClientHandle, evcClientDisconnected, 0, 0, 0, 0, 0);
+ FServer->DoEvent(WorkerSocket->ClientHandle, evcClientTerminated, 0, 0, 0, 0, 0);
+ delete WorkerSocket;
+ // Delete reference from list
+ FServer->Delete(Index);
+// LISTENER THREAD
+TMsgListenerThread::TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server)
+ FListener = Listener;
+ FreeOnTerminate = false;
+void TMsgListenerThread::Execute()
+ Sock = FListener->SckAccept(); // in any case we must accept
+ Valid = Sock != INVALID_SOCKET;
+// TCP SERVER
+TCustomMsgServer::TCustomMsgServer()
+ strcpy(FLocalAddress, "0.0.0.0");
+ CSList = new TSnapCriticalSection();
+ CSEvent = new TSnapCriticalSection();
+ FEventQueue = new TMsgEventQueue(MaxEvents, sizeof (TSrvEvent));
+ memset(Workers, 0, sizeof (Workers));
+ for (int i = 0; i < MaxWorkers; i++)
+ Workers[i] = NULL;
+ Status = SrvStopped;
+ EventMask = 0xFFFFFFFF;
+ LogMask = 0xFFFFFFFF;
+ FLastError = 0;
+ ClientsCount = 0;
+ LocalBind = 0;
+ MaxClients = MaxWorkers;
+ OnEvent = NULL;
+TCustomMsgServer::~TCustomMsgServer()
+ delete CSList;
+ delete CSEvent;
+ delete FEventQueue;
+void TCustomMsgServer::LockList()
+ CSList->Enter();
+void TCustomMsgServer::UnlockList()
+ CSList->Leave();
+int TCustomMsgServer::FirstFree()
+ for (i = 0; i < MaxWorkers; i++)
+ if (Workers[i] == 0)
+int TCustomMsgServer::StartListener()
+ strncpy(SockListener->LocalAddress, FLocalAddress, 16);
+ SockListener->LocalPort = LocalPort;
+ Result = SockListener->SckBind();
+ LocalBind = SockListener->LocalBind;
+ Result = SockListener->SckListen();
+ ServerThread = new TMsgListenerThread(SockListener, this);
+void TCustomMsgServer::TerminateAll()
+ bool Timeout;
+ if (ClientsCount > 0)
+ for (c = 0; c < MaxWorkers; c++)
+ if (Workers[c] != 0)
+ PMsgWorkerThread(Workers[c])->Terminate();
+ // Wait for closing
+ Elapsed = SysGetTick();
+ Timeout = false;
+ while (!Timeout && (ClientsCount > 0))
+ Timeout = DeltaTime(Elapsed) > WkTimeout;
+ if (!Timeout)
+ KillAll(); // one o more threads are hanged
+void TCustomMsgServer::KillAll()
+ int c, cnt = 0;
+ LockList();
+ PMsgWorkerThread(Workers[c])->Kill();
+ PMsgWorkerThread(Workers[c])->WorkerSocket->ForceClose();
+ delete PMsgWorkerThread(Workers[c]);
+ Workers[c] = 0;
+ UnlockList();
+ DoEvent(0, evcClientsDropped, 0, cnt, 0, 0, 0);
+bool TCustomMsgServer::CanAccept(socket_t Socket)
+ return ((MaxClients == 0) || (ClientsCount < MaxClients));
+PWorkerSocket TCustomMsgServer::CreateWorkerSocket(socket_t Sock)
+ // Creates a funny default class : a tcp echo worker
+ Result = new TEcoTcpWorker();
+void TCustomMsgServer::DoEvent(int Sender, longword Code, word RetCode, word Param1, word Param2, word Param3, word Param4)
+ TSrvEvent SrvEvent;
+ bool GoLog = (Code & LogMask) != 0;
+ bool GoEvent = (Code & EventMask) != 0;
+ if (!Destroying && (GoLog || GoEvent))
+ time(&SrvEvent.EvtTime);
+ SrvEvent.EvtSender = Sender;
+ SrvEvent.EvtCode = Code;
+ SrvEvent.EvtRetCode = RetCode;
+ SrvEvent.EvtParam1 = Param1;
+ SrvEvent.EvtParam2 = Param2;
+ SrvEvent.EvtParam3 = Param3;
+ SrvEvent.EvtParam4 = Param4;
+ if (GoEvent && (OnEvent != NULL))
+ OnEvent(FUsrPtr, &SrvEvent, sizeof (TSrvEvent));
+ if (GoLog)
+ FEventQueue->Insert(&SrvEvent);
+void TCustomMsgServer::Delete(int Index)
+ Workers[Index] = 0;
+ ClientsCount--;
+void TCustomMsgServer::Incoming(socket_t Sock)
+ PWorkerSocket WorkerSocket;
+ longword ClientHandle = Msg_GetSockAddr(Sock);
+ if (CanAccept(Sock))
+ // First position available in the thread buffer
+ idx = FirstFree();
+ if (idx >= 0)
+ // Creates the Worker and assigns it the connected socket
+ WorkerSocket = CreateWorkerSocket(Sock);
+ // Creates the Worker thread
+ Workers[idx] = new TMsgWorkerThread(WorkerSocket, this);
+ PMsgWorkerThread(Workers[idx])->Index = idx;
+ // Update the number
+ ClientsCount++;
+ // And Starts the worker
+ PMsgWorkerThread(Workers[idx])->Start();
+ DoEvent(WorkerSocket->ClientHandle, evcClientAdded, 0, 0, 0, 0, 0);
+ DoEvent(ClientHandle, evcClientNoRoom, 0, 0, 0, 0, 0);
+ DoEvent(ClientHandle, evcClientRejected, 0, 0, 0, 0, 0);
+int TCustomMsgServer::Start()
+ if (Status != SrvRunning)
+ Result = StartListener();
+ DoEvent(0, evcListenerCannotStart, Result, 0, 0, 0, 0);
+ Status = SrvError;
+ DoEvent(0, evcServerStarted, SockListener->ClientHandle, LocalPort, 0, 0, 0);
+ Status = SrvRunning;
+ FLastError = Result;
+int TCustomMsgServer::StartTo(const char *Address, word Port)
+ strncpy(FLocalAddress, Address, 16);
+ LocalPort = Port;
+void TCustomMsgServer::Stop()
+ if (Status == SrvRunning)
+ if (ServerThread->WaitFor(ThTimeout) != WAIT_OBJECT_0)
+ // Terminate all client threads
+ TerminateAll();
+ DoEvent(0, evcServerStopped, 0, 0, 0, 0, 0);
+int TCustomMsgServer::SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr)
+ OnEvent = PCallBack;
+ FUsrPtr = UsrPtr;
+bool TCustomMsgServer::PickEvent(void *pEvent)
+ return FEventQueue->Extract(pEvent);
+bool TCustomMsgServer::EventEmpty()
+ return FEventQueue->Empty();
+void TCustomMsgServer::EventsFlush()
+ FEventQueue->Flush();
@@ -0,0 +1,247 @@
+#ifndef snap_tcpsrvr_h
+#define snap_tcpsrvr_h
+#define MaxWorkers 1024
+#define MaxEvents 1500
+const int SrvStopped = 0;
+const int SrvRunning = 1;
+const int SrvError = 2;
+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 errSrvBase = 0x0000FFFF;
+const longword errSrvMask = 0xFFFF0000;
+const longword errSrvCannotStart = 0x00100000;
+const longword ThTimeout = 2000; // Thread timeout
+const longword WkTimeout = 3000; // Workers termination timeout
+typedef void (S7API *pfn_SrvCallBack)(void * usrPtr, PSrvEvent PEvent, int Size);
+class TMsgEventQueue
+ int IndexIn; // <-- insert index
+ int IndexOut; // --> extract index
+ int Max; // Buffer upper bound [0..Max]
+ int FCapacity; // Queue capacity
+ pbyte Buffer;
+ int FBlockSize;
+ TMsgEventQueue(const int Capacity, const int BlockSize);
+ ~TMsgEventQueue();
+ void Flush();
+ void Insert(void *lpdata);
+ bool Extract(void *lpdata);
+ bool Empty();
+ bool Full();
+typedef TMsgEventQueue *PMsgEventQueue;
+class TCustomMsgServer; // forward declaration
+// It's created when connection is accepted, it will interface with the client.
+class TMsgWorkerThread : public TSnapThread
+ TCustomMsgServer *FServer;
+ TMsgSocket *WorkerSocket;
+ friend class TCustomMsgServer;
+ TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server);
+typedef TMsgWorkerThread *PMsgWorkerThread;
+// It listens for incoming connection.
+class TMsgListenerThread : public TSnapThread
+ TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server);
+typedef TMsgListenerThread *PMsgListenerThread;
+typedef TMsgSocket *PWorkerSocket;
+class TCustomMsgServer
+ int FLastError;
+ PMsgListenerThread ServerThread;
+ // Critical section to lock Workers list activities
+ PSnapCriticalSection CSList;
+ // Event queue
+ PMsgEventQueue FEventQueue;
+ // Callback related
+ pfn_SrvCallBack OnEvent;
+ // private methods
+ int StartListener();
+ void LockList();
+ void UnlockList();
+ // Critical section to lock Event activities
+ PSnapCriticalSection CSEvent;
+ void *Workers[MaxWorkers];
+ // Terminates all worker threads
+ virtual void TerminateAll();
+ // Kills all worker threads that are unresponsive
+ void KillAll();
+ // if (true the connection is accepted, otherwise the connection
+ // is closed gracefully
+ virtual bool CanAccept(socket_t Socket);
+ // Returns the class of the worker socket, override it for real servers
+ virtual PWorkerSocket CreateWorkerSocket(socket_t Sock);
+ // Handles the event
+ virtual void DoEvent(int Sender, longword Code, word RetCode, word Param1,
+ // Delete the worker from the list (It's invoked by Worker Thread)
+ void Delete(int Index);
+ // Incoming connection (It's invoked by ServerThread, the listener)
+ virtual void Incoming(socket_t Sock);
+ friend class TMsgWorkerThread;
+ friend class TMsgListenerThread;
+ longword LogMask;
+ longword EventMask;
+ int ClientsCount;
+ int MaxClients;
+ TCustomMsgServer();
+ virtual ~TCustomMsgServer();
+ // Starts the server
+ int StartTo(const char *Address, word Port);
+ // Stops the server
+ int SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr);
+ // Pick an event from the circular queue
+ bool PickEvent(void *pEvent);
+ // Returns true if (the Event queue is empty
+ bool EventEmpty();
+ // Flushes Event queue
+ void EventsFlush();
+// TCP WORKER
+// Default worker class, a simply tcp echo to test the connection and
+// data I/O to use the server outside the project
+class TEcoTcpWorker : public TMsgSocket
+ bool Execute()
+ byte Buffer[4096];
+ if (CanRead(WorkInterval)) // Small time to avoid time wait during the close
+ Receive(&Buffer,sizeof(Buffer),Size);
+ if ((LastTcpError==0) && (Size>0))
+ SendPacket(&Buffer,Size);
+ return LastTcpError==0;
+#endif // snap_tcpsrvr_h
@@ -0,0 +1,162 @@
+DWORD WINAPI ThreadProc(LPVOID param)
+void* ThreadProc(void* param)
+ PSnapThread Thread;
+ // Unix but not Solaris
+#if (defined(POSIX) || defined(OS_OSX)) && (!defined(OS_SOLARIS_NATIVE_THREADS))
+ int last_type, last_state;
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &last_type);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &last_state);
+ Thread = PSnapThread(param);
+ if (!Thread->Terminated)
+ Thread->Execute();
+ Thread->Closed = true;
+ if (Thread->FreeOnTerminate)
+ delete Thread;
+ ExitThread(0);
+#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
+ pthread_exit((void*)0);
+#if defined(OS_OSX)
+#ifdef OS_SOLARIS_NATIVE_THREADS
+ thr_exit((void*)0);
+ return 0; // never reach, only to avoid compiler warning
+TSnapThread::TSnapThread()
+ Started = false;
+ Closed=false;
+ Terminated = false;
+TSnapThread::~TSnapThread()
+ if (Started && !Closed)
+ Terminate();
+ Join();
+ if (Started)
+ CloseHandle(th);
+void TSnapThread::ThreadCreate()
+ th = CreateThread(0, 0, ThreadProc, this, 0, 0);
+ pthread_attr_t a;
+ pthread_attr_init(&a);
+ pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+ pthread_create(&th, &a, &ThreadProc, this);
+ pthread_create(&th, 0, &ThreadProc, this);
+ thr_create(0, // default stack base
+ 0, // default stack size
+ &ThreadProc, // Thread routine
+ this, // argument
+ 0,
+ &th);
+void TSnapThread::Start()
+ if (!Started)
+ ThreadCreate();
+ Started = true;
+void TSnapThread::Terminate()
+ Terminated = true;
+void TSnapThread::Kill()
+ ThreadKill();
+ Closed = true;
+void TSnapThread::Join()
+ ThreadJoin();
+longword TSnapThread::WaitFor(uint64_t Timeout)
+ if (!Closed)
+ return ThreadWait(Timeout);
+ return WAIT_OBJECT_0;
@@ -0,0 +1,45 @@
+#ifndef snap_threads_h
+#define snap_threads_h
+# include "win_threads.h"
+# include "unix_threads.h"
+# include "sol_threads.h"
+#endif // snap_threads_h
@@ -0,0 +1,208 @@
+| Solaris 11 Threads support |
+#ifndef sol_threads_h
+#define sol_threads_h
+#include <thread.h>
+#include <synch.h>
+class TSnapCriticalSection {
+ mutex_t mx;
+ int result;
+ TSnapCriticalSection() {
+ mutex_init(&mx, USYNC_THREAD, 0);
+ ~TSnapCriticalSection() {
+ mutex_destroy(&mx);
+ void Enter() {
+ mutex_lock(&mx);
+ void Leave() {
+ mutex_unlock(&mx);
+ bool TryEnter() {
+ return mutex_trylock(&mx) == 0;
+typedef TSnapCriticalSection *PSnapCriticalSection;
+const longword WAIT_OBJECT_0 = 0x00000000L;
+const longword WAIT_ABANDONED = 0x00000080L;
+const longword WAIT_TIMEOUT = 0x00000102L;
+const longword WAIT_FAILED = 0xFFFFFFFFL;
+class TSnapEvent {
+ cond_t CVariable;
+ mutex_t Mutex;
+ bool AutoReset;
+ bool State;
+ TSnapEvent(bool ManualReset)
+ AutoReset = !ManualReset;
+ cond_init(&CVariable, USYNC_THREAD, 0) == 0;
+ mutex_init(&Mutex, USYNC_THREAD, 0);
+ State = false;
+ ~TSnapEvent()
+ cond_destroy(&CVariable);
+ mutex_destroy(&Mutex);
+ void Set()
+ mutex_lock(&Mutex);
+ State = true;
+ if (AutoReset)
+ cond_signal(&CVariable);
+ cond_broadcast(&CVariable);
+ mutex_unlock(&Mutex);
+ void Reset()
+ longword WaitForever()
+ while (!State) // <-- to avoid spurious wakeups
+ cond_wait(&CVariable, &Mutex);
+ longword WaitFor(int64_t Timeout)
+ longword Result = WAIT_OBJECT_0;
+ if (Timeout == 0)
+ Timeout = 1; // 0 is not allowed
+ if (Timeout > 0) {
+ if (!State) {
+ timespec ts;
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ uint64_t nsecs = ((uint64_t) tv.tv_sec) * 1000000000 +
+ Timeout * 1000000 +
+ ((uint64_t) tv.tv_usec) * 1000;
+ ts.tv_sec = nsecs / 1000000000;
+ ts.tv_nsec = (nsecs - ((uint64_t) ts.tv_sec) * 1000000000);
+ Result = cond_timedwait(&CVariable, &Mutex, &ts);
+ if (Result == ETIMEDOUT)
+ Result = WAIT_TIMEOUT;
+ while (Result == 0 && !State);
+ if (AutoReset) // take the ownership
+ else // Timeout<0
+ return WaitForever();
+typedef TSnapEvent *PSnapEvent;
+class TSnapThread {
+ thread_t th;
+ bool FCreateSuspended;
+ void ThreadCreate();
+ void ThreadJoin()
+ thr_join(th, 0, 0);
+ void ThreadKill()
+ thr_kill(th, 0);
+ longword ThreadWait(uint64_t Timeout)
+ longword Elapsed = SysGetTick();
+ while (!Closed && !(DeltaTime(Elapsed) > Timeout))
+ if (Closed)
+ return WAIT_TIMEOUT;
+ bool Started;
+ bool Terminated;
+ bool Closed;
+ bool FreeOnTerminate;
+ TSnapThread();
+ virtual ~TSnapThread();
+ virtual void Execute() {
+ void Start();
+ void Terminate();
+ void Kill();
+ void Join();
+ longword WaitFor(uint64_t Timeout);
+typedef TSnapThread *PSnapThread;
+#endif // sol_threads_h
@@ -0,0 +1,228 @@
+| Posix Threads support (Linux, FreeBSD) |
+#ifndef unix_threads_h
+#define unix_threads_h
+#include <semaphore.h>
+#include <pthread.h>
+class TSnapCriticalSection
+ pthread_mutex_t mx;
+// int result;
+ TSnapCriticalSection()
+ /*
+ This would be the best code, but very often it causes a segmentation fault in many
+ unix systems (the problem seems to be pthread_mutexattr_destroy()).
+ So, to avoid problems in future kernel/libc release, we use the "safe" default.
+ pthread_mutexattr_t mxAttr;
+ pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mx, &mxAttr);
+ pthread_mutexattr_destroy(&mxAttr);
+ pthread_mutex_init(&mx, 0);
+ ~TSnapCriticalSection()
+ pthread_mutex_destroy(&mx);
+ void Enter()
+ pthread_mutex_lock(&mx);
+ void Leave()
+ pthread_mutex_unlock(&mx);
+ bool TryEnter()
+ return pthread_mutex_trylock(&mx) == 0;
+class TSnapEvent
+ pthread_cond_t CVariable;
+ pthread_mutex_t Mutex;
+ if (pthread_cond_init(&CVariable, 0) == 0)
+ pthread_mutex_init(&Mutex, 0);
+ pthread_cond_destroy(&CVariable);
+ pthread_mutex_destroy(&Mutex);
+ pthread_mutex_lock(&Mutex);
+ pthread_cond_signal(&CVariable);
+ pthread_cond_broadcast(&CVariable);
+ pthread_mutex_unlock(&Mutex);
+ pthread_cond_wait(&CVariable, &Mutex);
+ if (Timeout > 0)
+ if (!State)
+ Result = pthread_cond_timedwait(&CVariable, &Mutex, &ts);
+ } while (Result == 0 && !State);
+class TSnapThread
+ pthread_t th;
+ pthread_join(th, 0);
+ pthread_cancel(th);
+ virtual void Execute()
+#endif // unix_threads_h
@@ -0,0 +1,159 @@
+| Windows Threads support (Windows, ReactOS) |
+#ifndef win_threads_h
+#define win_threads_h
+ CRITICAL_SECTION cs;
+ InitializeCriticalSection(&cs);
+ DeleteCriticalSection(&cs);
+ EnterCriticalSection(&cs);
+ LeaveCriticalSection(&cs);
+ return (TryEnterCriticalSection(&cs) != 0);
+ HANDLE Event;
+ Event = CreateEvent(0, ManualReset, false, 0);
+ if (Event != 0)
+ CloseHandle(Event);
+ SetEvent(Event);
+ ResetEvent(Event);
+ return WaitForSingleObject(Event, INFINITE);
+ return WAIT_FAILED;
+ longword WaitFor(int64_t Timeout) {
+ return WaitForSingleObject(Event, DWORD(Timeout));
+ HANDLE th;
+ WaitForSingleObject(th, INFINITE);
+ TerminateThread(th, 0);
+ return WaitForSingleObject(th, DWORD(Timeout));
+#endif // win_threads_h