Reinhard Russinger 6 years ago
commit
48ac5eebe2
69 changed files with 23105 additions and 0 deletions
  1. 58 0
      .gitignore
  2. 9 0
      README.txt
  3. 7 0
      build/README.txt
  4. 2 0
      build/bin/README.txt
  5. BIN
      build/bin/i586-linux/libsnap7.so
  6. 1 0
      build/temp/README.txt
  7. 111 0
      build/unix/common.mk
  8. 11 0
      build/unix/i586_linux.mk
  9. 120 0
      examples/cpp/apartner.cpp
  10. 515 0
      examples/cpp/client.cpp
  11. BIN
      examples/cpp/i586-linux/apartner
  12. BIN
      examples/cpp/i586-linux/client
  13. 33 0
      examples/cpp/i586-linux/makefile
  14. BIN
      examples/cpp/i586-linux/ppartner
  15. BIN
      examples/cpp/i586-linux/server
  16. BIN
      examples/cpp/i586-linux/server_v10
  17. BIN
      examples/cpp/i586-linux/srv_resourceless
  18. 165 0
      examples/cpp/ppartner.cpp
  19. 130 0
      examples/cpp/server.cpp
  20. 106 0
      examples/cpp/server_v10.cpp
  21. 775 0
      examples/cpp/snap7.cpp
  22. 957 0
      examples/cpp/snap7.h
  23. 200 0
      examples/cpp/srv_resourceless.cpp
  24. 141 0
      examples/plain-c/apartner.c
  25. 530 0
      examples/plain-c/client.c
  26. BIN
      examples/plain-c/i586-linux/apartner
  27. BIN
      examples/plain-c/i586-linux/client
  28. 30 0
      examples/plain-c/i586-linux/makefile
  29. BIN
      examples/plain-c/i586-linux/ppartner
  30. BIN
      examples/plain-c/i586-linux/server
  31. BIN
      examples/plain-c/i586-linux/srv_resourceless
  32. 171 0
      examples/plain-c/ppartner.c
  33. 114 0
      examples/plain-c/server.c
  34. 957 0
      examples/plain-c/snap7.h
  35. 203 0
      examples/plain-c/srv_resourceless.c
  36. 674 0
      gpl.txt
  37. 165 0
      lgpl-3.0.txt
  38. BIN
      release/Linux/i586/libsnap7.so
  39. 503 0
      src/core/s7_client.cpp
  40. 104 0
      src/core/s7_client.h
  41. 1256 0
      src/core/s7_firmware.h
  42. 541 0
      src/core/s7_isotcp.cpp
  43. 271 0
      src/core/s7_isotcp.h
  44. 3328 0
      src/core/s7_micro_client.cpp
  45. 364 0
      src/core/s7_micro_client.h
  46. 1178 0
      src/core/s7_partner.cpp
  47. 284 0
      src/core/s7_partner.h
  48. 122 0
      src/core/s7_peer.cpp
  49. 58 0
      src/core/s7_peer.h
  50. 2159 0
      src/core/s7_server.cpp
  51. 261 0
      src/core/s7_server.h
  52. 788 0
      src/core/s7_text.cpp
  53. 49 0
      src/core/s7_text.h
  54. 1066 0
      src/core/s7_types.h
  55. 129 0
      src/lib/snap7.def
  56. 1196 0
      src/lib/snap7_libmain.cpp
  57. 201 0
      src/lib/snap7_libmain.h
  58. 923 0
      src/sys/snap_msgsock.cpp
  59. 339 0
      src/sys/snap_msgsock.h
  60. 152 0
      src/sys/snap_platform.h
  61. 73 0
      src/sys/snap_sysutils.cpp
  62. 39 0
      src/sys/snap_sysutils.h
  63. 487 0
      src/sys/snap_tcpsrvr.cpp
  64. 247 0
      src/sys/snap_tcpsrvr.h
  65. 162 0
      src/sys/snap_threads.cpp
  66. 45 0
      src/sys/snap_threads.h
  67. 208 0
      src/sys/sol_threads.h
  68. 228 0
      src/sys/unix_threads.h
  69. 159 0
      src/sys/win_threads.h

+ 58 - 0
.gitignore

@@ -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*
+

+ 9 - 0
README.txt

@@ -0,0 +1,9 @@
+---------------------------------------------
+Snap7 IoT Package for Intel Quark architecure
+---------------------------------------------
+
+Devices Tested
+-------------------------
+Intel Galileo Gen 2
+Siemens SIMATIC IOT2040
+

+ 7 - 0
build/README.txt

@@ -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

+ 2 - 0
build/bin/README.txt

@@ -0,0 +1,2 @@
+These folders will contain binary libraries generated from project/makefiles.
+

BIN
build/bin/i586-linux/libsnap7.so


+ 1 - 0
build/temp/README.txt

@@ -0,0 +1 @@
+These folders contain intermediate objects/temp files, can be safety emptied...

+ 111 - 0
build/unix/common.mk

@@ -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)
+

+ 11 - 0
build/unix/i586_linux.mk

@@ -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

+ 120 - 0
examples/cpp/apartner.cpp

@@ -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)
+{
+#ifdef OS_WINDOWS
+	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);
+#endif
+}
+//------------------------------------------------------------------------------
+// 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());
+        return 1;
+    }           
+    // 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());
+            SysSleep(500);
+        } while (SndError == 0);
+    }
+}

+ 515 - 0
examples/cpp/client.cpp

@@ -0,0 +1,515 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Client Example                                                              |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "snap7.h"
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+     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
+//------------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+	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);
+#endif
+}
+//------------------------------------------------------------------------------
+//  Usage Syntax
+//------------------------------------------------------------------------------
+void Usage()
+{
+    printf("Usage\n");
+    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
+#endif
+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);
+    printf("+-----------------------------------------------------\n");
+    if (Result==0) {
+        printf("| Result         : OK\n");
+        printf("| Execution time : %d ms\n",Client->ExecTime());
+        printf("+-----------------------------------------------------\n");
+        ok++;
+    }
+    else {
+        printf("| ERROR !!! \n");
+        if (Result<0)
+            printf("| Library Error (-1)\n");
+        else
+            printf("| %s\n",CliErrorText(Result).c_str());
+        printf("+-----------------------------------------------------\n");
+        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()
+{
+     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.
+     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)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// Async Upload DB0 (using event wait as completion trigger)
+//------------------------------------------------------------------------------
+void AsEWUploadDB0()
+{
+     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.
+     JobDone=false;
+     int res=Client->AsUpload(Block_SDB, 0, &Buffer, &Size);
+     
+     if (res==0)
+     {
+         res=Client->WaitAsCompletion(3000);
+     }    
+
+     if (Check(res,"Async (Wait event) Block Upload (SDB 0)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// Async Upload DB0 (using polling as completion trigger)
+//------------------------------------------------------------------------------
+void AsPOUploadDB0()
+{
+     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.
+     JobDone=false;
+     int res=Client->AsUpload(Block_SDB, 0, &Buffer, &Size);
+     
+     if (res==0)
+     {
+         while (!Client->CheckAsCompletion(&res))
+         {
+             SysSleep(100);
+         };         
+     }    
+
+     if (Check(res,"Async (polling) Block Upload (SDB 0)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// 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);
+        printf("Dump (%d bytes) :\n",Size);
+        hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// 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("\n");
+    printf("+-----------------------------------------------------\n");
+    printf("| Test Summary \n");
+    printf("+-----------------------------------------------------\n");
+    printf("| Performed : %d\n",(ok+ko));
+    printf("| Passed    : %d\n",ok);
+    printf("| Failed    : %d\n",ko);
+    printf("+----------------------------------------[press a key]\n");
+    getchar();
+}
+//------------------------------------------------------------------------------
+// Main              
+//------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+// Get Progran args (we need the client address and optionally Rack and Slot)  
+    if (argc!=2 && argc!=4)
+    {
+        Usage();
+        return 1;
+    }
+    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;
+}
+

BIN
examples/cpp/i586-linux/apartner


BIN
examples/cpp/i586-linux/client


+ 33 - 0
examples/cpp/i586-linux/makefile

@@ -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
+
+CXX      := g++
+CC       := gcc
+CXXFLAGS :=-O3
+CFLAGS   :=
+
+.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) 
+
+clean:
+	$(RM) client
+	$(RM) server
+	$(RM) server_v10
+	$(RM) srv_resourceless
+	$(RM) apartner
+	$(RM) ppartner

BIN
examples/cpp/i586-linux/ppartner


BIN
examples/cpp/i586-linux/server


BIN
examples/cpp/i586-linux/server_v10


BIN
examples/cpp/i586-linux/srv_resourceless


+ 165 - 0
examples/cpp/ppartner.cpp

@@ -0,0 +1,165 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Passive Partner Example                                                     |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "snap7.h"
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+	TS7Partner *Partner;
+	byte Buffer[65536]; // 64 K buffer
+
+//------------------------------------------------------------------------------
+// Usage syntax
+//------------------------------------------------------------------------------
+void Usage()
+{
+	printf("Usage\n");
+	printf("  PPartner <ActiveIP>\n");
+	printf("Where\n");
+	printf("  <ActiveIP> is the address of the active partner that we are waiting for.\n");
+	printf("Note\n");
+	printf("- Local Address is set to 0.0.0.0 (the default adapter)\n");
+	printf("- Both Local TSAP and Remote TSAP are set to 0x1002\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");
+	getchar();
+}
+//------------------------------------------------------------------------------
+// SysSleep (copied from snap_sysutils.cpp)
+//------------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+    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);
+#endif
+}
+//------------------------------------------------------------------------------
+// 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
+#endif
+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');
+			}
+	}
+}
+//------------------------------------------------------------------------------
+// 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);
+}
+//------------------------------------------------------------------------------
+// Main
+//------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+        // Get Progran args
+		if (argc != 2)
+		{
+			Usage();
+			return 1;
+		}
+		// Create the PASSIVE partner
+		Partner = new TS7Partner(false);
+		// Set the BRecv callback
+		Partner->SetRecvCallback(RecvCallback, NULL);
+		// Start
+		int Error=Partner->StartTo("0.0.0.0", argv[1], 0x1002, 0x1002);
+		if (Error == 0)
+		    printf("Passive partner started\n");
+		else
+		    printf("%s\n",ParErrorText(Error).c_str());
+		// 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".
+		getchar();
+		Partner->Stop();
+		delete Partner;
+}

+ 130 - 0
examples/cpp/server.cpp

@@ -0,0 +1,130 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  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 <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+#include "snap7.h"
+
+     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
+    printf("%s\n",SrvEventText(PEvent).c_str());
+	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 main(int argc, char* argv[])
+{
+    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
+        getchar();
+    }
+    else
+        printf("%s\n",SrvErrorText(Error).c_str());
+
+    // 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".
+
+    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 :
+/*
+int main(int argc, char* argv[])
+{
+   TS7Server *Server = new TS7Server;
+   Server->Start();
+   getchar();
+   delete Server;
+}
+*/

+ 106 - 0
examples/cpp/server_v10.cpp

@@ -0,0 +1,106 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.0.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Server Example                                                              |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+#include "snap7.h"
+
+     TS7Server *Server;
+     unsigned char DB1[512];  // Our DB1
+     unsigned char DB2[128];  // Our DB2
+     unsigned char DB3[1024]; // Our DB3
+
+// 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());
+};
+
+int main(int argc, char* argv[])
+{
+    int Error;
+    Server = new TS7Server;
+
+    // Share some resources with our virtual PLC
+    Server->RegisterArea(srvAreaDB,     // We are registering a DB
+                         1,             // Its number is 1 (DB1)
+                         &DB1,          // Our buffer for DB1
+                         sizeof(DB1));  // Its size
+    // Do the same for DB2 and DB3
+    Server->RegisterArea(srvAreaDB, 2, &DB2, sizeof(DB2));
+    Server->RegisterArea(srvAreaDB, 3, &DB3, sizeof(DB3));
+
+    // Set the event callback to show something : it's not strictly needed.
+    // If you comment next line the server still works fine.
+    Server->SetEventsCallback(EventCallBack, 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
+        getchar();
+    }
+    else
+        printf("%s\n",SrvErrorText(Error).c_str());
+
+    // 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".
+
+    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 :
+/*
+int main(int argc, char* argv[])
+{
+   TS7Server *Server = new TS7Server;
+   Server->Start();
+   getchar();
+   delete Server;
+}
+*/

+ 775 - 0
examples/cpp/snap7.cpp

@@ -0,0 +1,775 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  C++ Snap 7 classes Implementation                                           |
+|                                                                              |
+|=============================================================================*/
+
+#include "snap7.h"
+
+//==============================================================================
+// 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;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+int TS7Client::LastError()
+{
+    int LastError;
+    int Result =Cli_GetLastError(Client, &LastError);
+    if (Result==0)
+       return LastError;
+    else
+       return Result;
+}
+//---------------------------------------------------------------------------
+int TS7Client::PDULength()
+{
+    int Requested, Negotiated;
+    if (Cli_GetPduLength(Client, &Requested, &Negotiated)==0)
+        return Negotiated;
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+int TS7Client::PDURequested()
+{
+    int Requested, Negotiated;
+    if (Cli_GetPduLength(Client, &Requested, &Negotiated)==0)
+        return Requested;
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+int TS7Client::PlcStatus()
+{
+    int Status;
+    int Result = Cli_GetPlcStatus(Client, &Status);
+    if (Result==0)
+        return Status;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+bool TS7Client::Connected()
+{
+	int ClientStatus;
+	if (Cli_GetConnected(Client ,&ClientStatus)==0)
+		return ClientStatus!=0;
+	else
+		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;
+    else
+       return false;
+}
+//---------------------------------------------------------------------------
+void TS7Server::ClearEvents()
+{
+    Srv_ClearEvents(Server);
+}
+//---------------------------------------------------------------------------
+longword TS7Server::GetEventsMask()
+{
+    longword Mask;
+    int Result = Srv_GetMask(Server, mkEvent, &Mask);
+    if (Result==0)
+        return Mask;
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+longword TS7Server::GetLogMask()
+{
+    longword Mask;
+    int Result = Srv_GetMask(Server, mkLog, &Mask);
+    if (Result==0)
+        return Mask;
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+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);
+    if (Result==0)
+        return ServerStatus;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+int TS7Server::GetCpuStatus()
+{
+    int ServerStatus, CpuStatus, ClientsCount;
+    int Result =Srv_GetStatus(Server, &ServerStatus, &CpuStatus, &ClientsCount);
+    if (Result==0)
+            return CpuStatus;
+    else
+            return Result;
+}
+//---------------------------------------------------------------------------
+int TS7Server::ClientsCount()
+{
+    int ServerStatus, CpuStatus, ClientsCount;
+    int Result =Srv_GetStatus(Server, &ServerStatus, &CpuStatus, &ClientsCount);
+    if (Result==0)
+        return ClientsCount;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+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);
+    if (Result==0)
+        return ParStatus;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+int TS7Partner::LastError()
+{
+    int Error;
+    int Result = Par_GetLastError(Partner, &Error);
+    if (Result==0)
+        return Error;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+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)
+{
+    char text[TextLen];
+    Srv_ErrorText(Error, text, TextLen);
+    return TextString(text);
+}
+//---------------------------------------------------------------------------
+TextString ParErrorText(int Error)
+{
+    char text[TextLen];
+    Par_ErrorText(Error, text, TextLen);
+    return TextString(text);
+}
+//---------------------------------------------------------------------------
+TextString SrvEventText(TSrvEvent *Event)
+{
+    char text[TextLen];
+    Srv_EventText(Event, text, TextLen);
+    return TextString(text);
+}
+

+ 957 - 0
examples/cpp/snap7.h

@@ -0,0 +1,957 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  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
+#endif
+
+// Visual Studio needs this to use the correct time_t size
+#if defined (_WIN32) && !defined(_WIN64)
+# define _USE_32BIT_TIME_T 
+#endif
+
+#if defined(unix) || defined(__unix__) || defined(__unix)
+# define PLATFORM_UNIX
+#endif
+
+#if defined(__SVR4) || defined(__svr4__)
+# define OS_SOLARIS
+#endif
+
+#if BSD>=0
+# define OS_BSD
+#endif
+
+#if defined(__APPLE__)
+# define OS_OSX
+#endif
+
+#if defined(PLATFORM_UNIX) || defined(OS_OSX)
+# include <unistd.h>
+# if defined(_POSIX_VERSION)
+#   define POSIX
+# endif
+#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
+# endif
+#else
+# include <stdint.h>
+#endif
+
+extern "C" {
+#endif
+//---------------------------------------------------------------------------
+// C exact length types
+//---------------------------------------------------------------------------
+#ifndef __cplusplus
+
+#ifdef OS_BSD
+#  include <stdint.h>
+#  include <time.h>
+#endif
+
+#ifdef OS_OSX
+#  include <stdint.h>  
+#  include <time.h>
+#endif
+
+#ifdef OS_SOLARIS
+#  include <stdint.h>  
+#  include <time.h>
+#endif
+
+#if defined(_UINTPTR_T_DEFINED)
+#  include <stdint.h>
+#  include <time.h>
+#endif
+
+#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
+#endif
+
+#endif
+
+#ifdef OS_WINDOWS
+# define S7API __stdcall
+#else
+# define S7API
+#endif
+
+#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.
+
+#ifndef __cplusplus
+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;
+#endif
+
+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;
+
+//******************************************************************************
+//                                   CLIENT
+//******************************************************************************
+
+// 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
+typedef struct{
+   int   Area;
+   int   WordLen;
+   int   Result;
+   int   DBNumber;
+   int   Start;
+   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
+typedef struct {
+   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
+typedef struct {
+   char Code[21];
+   byte V1;
+   byte V2;
+   byte V3;
+} TS7OrderCode, *PS7OrderCode;
+
+// CPU Info
+typedef struct {
+   char ModuleTypeName[33];
+   char SerialNumber[25];
+   char ASName[25];
+   char Copyright[27];
+   char ModuleName[25];
+} TS7CpuInfo, *PS7CpuInfo;
+
+// CP Info
+typedef struct {
+   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
+typedef struct {
+   word LENTHDR;
+   word N_DR;
+} SZL_HEADER, *PSZL_HEADER;
+
+typedef struct {
+   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
+typedef struct {
+   SZL_HEADER Header;
+   word List[0x2000-2];
+} TS7SZLList, *PS7SZLList;
+
+// See §33.19 of "System Software for S7-300/400 System and Standard Functions"
+typedef struct {
+   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);
+
+//******************************************************************************
+//                                   SERVER
+//******************************************************************************
+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;
+
+typedef struct{
+	time_t EvtTime;    // Timestamp
+	int EvtSender;     // Sender
+	longword EvtCode;  // Event code
+	word EvtRetCode;   // Event result
+	word EvtParam1;    // Param 1 (if available)
+	word EvtParam2;    // Param 2 (if available)
+	word EvtParam3;    // Param 3 (if available)
+	word EvtParam4;    // Param 4 (if available)
+}TSrvEvent, *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);
+
+//******************************************************************************
+//                                   PARTNER
+//******************************************************************************
+
+// 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
+
+// Errors
+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()
+#ifdef __cplusplus
+ }
+#endif // __cplusplus
+
+#ifdef __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);
+	// Control functions
+	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
+{
+private:
+    S7Object Server;
+public:
+    TS7Server();
+    ~TS7Server();
+    // Control
+    int Start();
+    int StartTo(const char *Address);
+    int Stop();
+    int GetParam(int ParamNumber, void *pValue);
+    int SetParam(int ParamNumber, void *pValue);
+    // 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);
+    // Properties
+    int ServerStatus();
+    int GetCpuStatus();
+    int SetCpuStatus(int Status);
+	int ClientsCount();
+};
+typedef TS7Server *PS7Server;
+
+//******************************************************************************
+//                          PARTNER CLASS DEFINITION
+//******************************************************************************
+class TS7Partner
+{
+private:
+	S7Object Partner; // Partner Handle
+public:
+	TS7Partner(bool Active);
+	~TS7Partner();
+	// Control
+	int GetParam(int ParamNumber, void *pValue);
+	int SetParam(int ParamNumber, void *pValue);
+	int Start();
+	int StartTo(const char *LocalAddress,
+				const char *RemoteAddress,
+				int LocalTSAP,
+				int RemoteTSAP);
+	int Stop();
+	// 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);
+	// Properties
+	int Status();
+	int LastError();
+	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 // __cplusplus
+#endif // snap7_h

+ 200 - 0
examples/cpp/srv_resourceless.cpp

@@ -0,0 +1,200 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  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.                                                    |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include "snap7.h"
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+	TS7Server *Server;
+	typedef byte TRWBuffer[1024];
+	typedef byte *PRWBuffer;
+	byte cnt = 0;
+//------------------------------------------------------------------------------
+// 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
+#endif
+	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');
+			}
+		}
+	}
+
+//------------------------------------------------------------------------------
+// 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");
+	else
+		printf("Write Request\n");
+
+	switch (PTag->Area)
+	{
+		case S7AreaPE: printf("Area : PE, ");
+			break;
+		case S7AreaPA: printf("Area : PA, ");
+			break;
+		case S7AreaMK: printf("Area : MK, ");
+			break;
+		case S7AreaCT: printf("Area : CT, ");
+			break;
+		case S7AreaTM: printf("Area : TM, ");
+			break;
+		case S7AreaDB: printf("Area : DB%d, ", PTag->DBNumber);
+			break;
+		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);
+	else
+	{
+		for (int c = 0; c < 1024; c++)
+			PBuffer[c] = cnt;
+		cnt++;
+
+	}
+	printf("\n");
+	return 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());
+};
+
+int main(int argc, char* argv[])
+{
+	int Error;
+	Server = new TS7Server;
+	// Filter a bit of noise
+	Server->SetEventsMask(0x3ff);
+	// Set the Read/Write callback 
+	Server->SetRWAreaCallback(RWAreaCallBack, NULL);
+	// Set the event callback to show something : it's not strictly needed.
+	// If you comment next line the server still works fine.
+	Server->SetEventsCallback(EventCallBack, 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
+		getchar();
+	}
+	else
+		printf("%s\n", SrvErrorText(Error).c_str());
+
+	// 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".
+
+	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 :
+/*
+int main(int argc, char* argv[])
+{
+	TS7Server *Server = new TS7Server;
+	Server->Start();
+	getchar();
+	delete Server;
+}
+*/

+ 141 - 0
examples/plain-c/apartner.c

@@ -0,0 +1,141 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Active Partner Example                                                      |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "snap7.h"
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+#define size 256
+
+    int cnt = 0;
+	byte Buffer[size]; 
+	S7Object 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()
+{
+    int i;
+	cnt++;
+    for (i = 0; i < size; i++)
+        Buffer[i] = cnt;
+}
+//------------------------------------------------------------------------------
+// SysSleep (copied from snap_sysutils.cpp)
+//------------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+	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);
+#endif
+}
+//------------------------------------------------------------------------------
+bool ParLinked()
+{
+	int ParStatus;
+	Par_GetStatus(Partner, &ParStatus);
+	return ParStatus==par_linked;
+}
+//------------------------------------------------------------------------------
+void PrintError(int Error)
+{
+	char text[1024];
+	Par_ErrorText(Partner, text, 1024);
+	printf("%s\n",text);
+}
+//------------------------------------------------------------------------------
+// Main
+//------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+    int SndError = 0;
+	int Error;
+    // Get Progran args
+    if (argc!=2)
+    {
+        Usage();
+        return 1;
+    };
+    // Create the ACTIVE partner
+    Partner = Par_Create(1);
+    // Start
+    // Local Address for an active partner is meaningless, leave
+    // it always set to "0.0.0.0"
+    Error=Par_StartTo(Partner, "0.0.0.0", argv[1], 0x1002, 0x1002);
+    if (Error != 0)
+    {
+		PrintError(Error);
+        return 1;
+    }           
+    // Endless loop : Exit with Ctrl-C       
+    while (1)
+    {
+        while (!ParLinked())
+        {
+            printf("Connecting to %s ...\n",argv[1]);
+            SysSleep(500);
+        };
+        do
+        {
+            PrepareBuffer();
+            SndError = Par_BSend(Partner, 0x00000001, &Buffer, size);
+            if (SndError == 0)
+                printf("Succesfully sent %d bytes\n",size);
+            else
+                PrintError(SndError);
+            SysSleep(500);
+        } while (SndError == 0);
+    }
+}

+ 530 - 0
examples/plain-c/client.c

@@ -0,0 +1,530 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Client Example                                                              |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "snap7.h"
+
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+    S7Object Client;
+    unsigned char 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
+
+    int 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
+//------------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+	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);
+#endif
+}
+//------------------------------------------------------------------------------
+//  Usage Syntax
+//------------------------------------------------------------------------------
+void Usage()
+{
+    printf("Usage\n");
+    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
+#endif
+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
+//------------------------------------------------------------------------------
+int Check(int Result, char * function)
+{
+    int ExecTime;
+	char text[1024];
+    printf("\n");
+    printf("+-----------------------------------------------------\n");
+    printf("| %s\n",function);
+    printf("+-----------------------------------------------------\n");
+    if (Result==0) {
+        Cli_GetExecTime(Client, &ExecTime);
+        printf("| Result         : OK\n");
+        printf("| Execution time : %d ms\n",ExecTime);
+        printf("+-----------------------------------------------------\n");
+        ok++;
+    }
+    else {
+        printf("| ERROR !!! \n");
+        if (Result<0)
+            printf("| Library Error (-1)\n");
+        else
+		{
+			Cli_ErrorText(Result, text, 1024);
+			printf("| %s\n",text);
+		}
+        printf("+-----------------------------------------------------\n");
+        ko++;
+    }
+    return !Result;
+}
+//------------------------------------------------------------------------------
+// Multi Read
+//------------------------------------------------------------------------------
+void MultiRead()
+{
+     int res;
+
+     // 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;
+
+     res=Cli_ReadMultiVars(Client, &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=Cli_ListBlocks(Client, &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=Cli_GetOrderCode(Client, &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=Cli_GetCpuInfo(Client, &Info);
+     if (Check(res,"Unit Info"))
+     {
+          printf("  Module Type Name : %s\n",Info.ModuleTypeName);
+          printf("  Seriel 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=Cli_GetCpInfo(Client, &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;
+     Cli_GetPlcStatus(Client, &Status);
+     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=Cli_Upload(Client, 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()
+{
+     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;
+	 JobDone=false;
+     
+	 res=Cli_AsUpload(Client, Block_SDB, 0, &Buffer, &Size);
+     
+     if (res==0)
+     {
+         while (!JobDone)
+         {
+             SysSleep(100);
+         }
+         res=JobResult;
+     }    
+
+     if (Check(res,"Async (callback) Block Upload (SDB 0)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// Async Upload DB0 (using event wait as completion trigger)
+//------------------------------------------------------------------------------
+void AsEWUploadDB0()
+{
+     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;
+	 JobDone=false;
+     
+	 res=Cli_AsUpload(Client, Block_SDB, 0, &Buffer, &Size);
+     
+     if (res==0)
+     {
+         res=Cli_WaitAsCompletion(Client,3000);
+     }    
+
+     if (Check(res,"Async (Wait event) Block Upload (SDB 0)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// Async Upload DB0 (using polling as completion trigger)
+//------------------------------------------------------------------------------
+void AsPOUploadDB0()
+{
+     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;
+	 JobDone=false;
+     
+	 res=Cli_AsUpload(Client, Block_SDB, 0, &Buffer, &Size);
+     
+     if (res==0)
+     {
+		 while (Cli_CheckAsCompletion(Client,&res)!=JobComplete)
+         {
+             SysSleep(100);
+         };         
+     }    
+
+     if (Check(res,"Async (polling) Block Upload (SDB 0)"))
+     {
+          printf("Dump (%d bytes) :\n",Size);
+          hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// 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=Cli_ReadSZL(Client, 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);
+        printf("Dump (%d bytes) :\n",Size);
+        hexdump(&Buffer,Size);
+     }
+}
+//------------------------------------------------------------------------------
+// Unit Connection
+//------------------------------------------------------------------------------
+int CliConnect()
+{
+    int Requested, Negotiated, res;
+
+    res = Cli_ConnectTo(Client, Address,Rack,Slot);
+    if (Check(res,"UNIT Connection")) {
+          Cli_GetPduLength(Client, &Requested, &Negotiated);
+          printf("  Connected to   : %s (Rack=%d, Slot=%d)\n",Address,Rack,Slot);
+          printf("  PDU Requested  : %d bytes\n",Requested);
+          printf("  PDU Negotiated : %d bytes\n",Negotiated);
+    };
+    return !res;
+}
+//------------------------------------------------------------------------------
+// Unit Disconnection
+//------------------------------------------------------------------------------
+void CliDisconnect()
+{
+     Cli_Disconnect(Client);
+}
+//------------------------------------------------------------------------------
+// Perform readonly tests, no cpu status modification
+//------------------------------------------------------------------------------
+void PerformTests()
+{
+     OrderCode();
+     CpuInfo();
+     CpInfo();
+     UnitStatus();
+     ReadSzl_0011_0000();
+     UploadDB0();
+     AsCBUploadDB0();
+     AsEWUploadDB0();
+     AsPOUploadDB0();
+     MultiRead();
+}
+
+void Summary()
+{
+    printf("\n");
+    printf("+-----------------------------------------------------\n");
+    printf("| Test Summary \n");
+    printf("+-----------------------------------------------------\n");
+    printf("| Performed : %d\n",(ok+ko));
+    printf("| Passed    : %d\n",ok);
+    printf("| Failed    : %d\n",ko);
+    printf("+----------------------------------------[press a key]\n");
+    getchar();
+}
+
+int main(int argc, char* argv[])
+{
+// Get Progran args
+    if (argc!=2 && argc!=4)
+    {
+        Usage();
+        return 1;
+    }
+    Address=argv[1];
+    if (argc==4)
+    {
+        Rack=atoi(argv[2]);
+        Slot=atoi(argv[3]);
+    }
+
+// Client Creation
+	Client=Cli_Create();
+    Cli_SetAsCallback(Client, CliCompletion,NULL);
+
+// Connection
+    if (CliConnect())
+    {
+        PerformTests();
+        CliDisconnect();
+    };
+
+// Deletion
+    Cli_Destroy(&Client);
+    Summary();
+
+    return 0;
+}

BIN
examples/plain-c/i586-linux/apartner


BIN
examples/plain-c/i586-linux/client


+ 30 - 0
examples/plain-c/i586-linux/makefile

@@ -0,0 +1,30 @@
+##
+## 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 
+
+CXX      := g++
+CC       := gcc
+CXXFLAGS :=-O3
+CFLAGS   :=
+
+.PHONY: all clean
+
+all: 
+	$(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) 
+
+clean:
+	$(RM) client
+	$(RM) server
+	$(RM) srv_resourceless
+	$(RM) apartner
+	$(RM) ppartner

BIN
examples/plain-c/i586-linux/ppartner


BIN
examples/plain-c/i586-linux/server


BIN
examples/plain-c/i586-linux/srv_resourceless


+ 171 - 0
examples/plain-c/ppartner.c

@@ -0,0 +1,171 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Passive Partner Example                                                     |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "snap7.h"
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+	S7Object Partner;
+	byte Buffer[65536]; // 64 K buffer
+
+//------------------------------------------------------------------------------
+// Usage syntax
+//------------------------------------------------------------------------------
+void Usage()
+{
+	printf("Usage\n");
+	printf("  PPartner <ActiveIP>\n");
+	printf("Where\n");
+	printf("  <ActiveIP> is the address of the active partner that we are waiting for.\n");
+	printf("Note\n");
+	printf("- Local Address is set to 0.0.0.0 (the default adapter)\n");
+	printf("- Both Local TSAP and Remote TSAP are set to 0x1002\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");
+	getchar();
+}
+//------------------------------------------------------------------------------
+// SysSleep (copied from snap_sysutils.cpp)
+//------------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+    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);
+#endif
+}
+//------------------------------------------------------------------------------
+// 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
+#endif
+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');
+			}
+	}
+}
+//------------------------------------------------------------------------------
+// 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);
+}
+//------------------------------------------------------------------------------
+// Main
+//------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+        int Error;
+		char text[1024];
+	    // Get Progran args
+		if (argc != 2)
+		{
+			Usage();
+			return 1;
+		}
+		// Create the PASSIVE partner
+		Partner = Par_Create(0);
+		// Set the BRecv callback
+		Par_SetRecvCallback(Partner, RecvCallback, NULL);
+		// Start
+		Error=Par_StartTo(Partner, "0.0.0.0", argv[1], 0x1002, 0x1002);
+		if (Error == 0)
+		    printf("Passive partner started\n");
+		else
+		{
+		    Par_ErrorText(Error, text, 1024);
+			printf("%s\n",text);
+		};
+		// 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".
+		getchar();
+		Par_Stop(Partner);
+		Par_Destroy(&Partner);
+}

+ 114 - 0
examples/plain-c/server.c

@@ -0,0 +1,114 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Server Example                                                              |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "snap7.h"
+
+     S7Object Server;
+     unsigned char DB1[512];  // Our DB1
+     unsigned char DB2[128];  // Our DB2
+     unsigned char DB3[1024]; // Our DB3
+
+// 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
+	char text[1024];
+	Srv_EventText(PEvent, text, 1024);
+    printf("%s\n",text);
+};
+
+int main(int argc, char* argv[])
+{
+    int Error;
+	char text[1024];
+
+	Server = Srv_Create();
+
+    // Share some resources with our virtual PLC
+    Srv_RegisterArea(Server,
+		     srvAreaDB,     // We are registering a DB
+                     1,             // Its number is 1 (DB1)
+                     &DB1,          // Our buffer for DB1
+                     sizeof(DB1));  // Its size
+    // Do the same for DB2 and DB3
+    Srv_RegisterArea(Server, srvAreaDB, 2, &DB2, sizeof(DB2));
+    Srv_RegisterArea(Server, srvAreaDB, 3, &DB3, sizeof(DB3));
+
+    // Set the event callback to show something : it's not strictly needed.
+    // If you comment next line the server still works fine.
+    Srv_SetEventsCallback(Server, EventCallBack, NULL);
+
+    // Start the server onto the default adapter.
+    // To select an adapter we have to use Srv_StartTo(Server, "192.168.x.y").
+    // Start() is the same of StartTo("0.0.0.0");
+    Error=Srv_Start(Server);
+    if (Error==0)
+    {
+	// Now the server is running ... wait a key to terminate
+        getchar();
+    }
+    else
+	{
+        Srv_ErrorText(Error, text, 1024);
+        printf("%s\n", text);
+	}
+
+    // 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".
+
+    Srv_Stop(Server); // <- not strictly needed, every server is stopped on deletion
+                    //    and every client is gracefully disconnected.
+	Srv_Destroy(&Server);
+}
+
+// Finally, this is a very minimalist (but working) server :
+/*
+int main(int argc, char* argv[])
+{
+   TS7Server *Server = new TS7Server;
+   Server->Start();
+   getchar();
+   delete Server;
+}
+*/

+ 957 - 0
examples/plain-c/snap7.h

@@ -0,0 +1,957 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  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
+#endif
+
+// Visual Studio needs this to use the correct time_t size
+#if defined (_WIN32) && !defined(_WIN64)
+# define _USE_32BIT_TIME_T 
+#endif
+
+#if defined(unix) || defined(__unix__) || defined(__unix)
+# define PLATFORM_UNIX
+#endif
+
+#if defined(__SVR4) || defined(__svr4__)
+# define OS_SOLARIS
+#endif
+
+#if BSD>=0
+# define OS_BSD
+#endif
+
+#if defined(__APPLE__)
+# define OS_OSX
+#endif
+
+#if defined(PLATFORM_UNIX) || defined(OS_OSX)
+# include <unistd.h>
+# if defined(_POSIX_VERSION)
+#   define POSIX
+# endif
+#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
+# endif
+#else
+# include <stdint.h>
+#endif
+
+extern "C" {
+#endif
+//---------------------------------------------------------------------------
+// C exact length types
+//---------------------------------------------------------------------------
+#ifndef __cplusplus
+
+#ifdef OS_BSD
+#  include <stdint.h>
+#  include <time.h>
+#endif
+
+#ifdef OS_OSX
+#  include <stdint.h>  
+#  include <time.h>
+#endif
+
+#ifdef OS_SOLARIS
+#  include <stdint.h>  
+#  include <time.h>
+#endif
+
+#if defined(_UINTPTR_T_DEFINED)
+#  include <stdint.h>
+#  include <time.h>
+#endif
+
+#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
+#endif
+
+#endif
+
+#ifdef OS_WINDOWS
+# define S7API __stdcall
+#else
+# define S7API
+#endif
+
+#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.
+
+#ifndef __cplusplus
+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;
+#endif
+
+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;
+
+//******************************************************************************
+//                                   CLIENT
+//******************************************************************************
+
+// 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
+typedef struct{
+   int   Area;
+   int   WordLen;
+   int   Result;
+   int   DBNumber;
+   int   Start;
+   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
+typedef struct {
+   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
+typedef struct {
+   char Code[21];
+   byte V1;
+   byte V2;
+   byte V3;
+} TS7OrderCode, *PS7OrderCode;
+
+// CPU Info
+typedef struct {
+   char ModuleTypeName[33];
+   char SerialNumber[25];
+   char ASName[25];
+   char Copyright[27];
+   char ModuleName[25];
+} TS7CpuInfo, *PS7CpuInfo;
+
+// CP Info
+typedef struct {
+   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
+typedef struct {
+   word LENTHDR;
+   word N_DR;
+} SZL_HEADER, *PSZL_HEADER;
+
+typedef struct {
+   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
+typedef struct {
+   SZL_HEADER Header;
+   word List[0x2000-2];
+} TS7SZLList, *PS7SZLList;
+
+// See §33.19 of "System Software for S7-300/400 System and Standard Functions"
+typedef struct {
+   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);
+
+//******************************************************************************
+//                                   SERVER
+//******************************************************************************
+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;
+
+typedef struct{
+	time_t EvtTime;    // Timestamp
+	int EvtSender;     // Sender
+	longword EvtCode;  // Event code
+	word EvtRetCode;   // Event result
+	word EvtParam1;    // Param 1 (if available)
+	word EvtParam2;    // Param 2 (if available)
+	word EvtParam3;    // Param 3 (if available)
+	word EvtParam4;    // Param 4 (if available)
+}TSrvEvent, *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);
+
+//******************************************************************************
+//                                   PARTNER
+//******************************************************************************
+
+// 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
+
+// Errors
+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()
+#ifdef __cplusplus
+ }
+#endif // __cplusplus
+
+#ifdef __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);
+	// Control functions
+	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
+{
+private:
+    S7Object Server;
+public:
+    TS7Server();
+    ~TS7Server();
+    // Control
+    int Start();
+    int StartTo(const char *Address);
+    int Stop();
+    int GetParam(int ParamNumber, void *pValue);
+    int SetParam(int ParamNumber, void *pValue);
+    // 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);
+    // Properties
+    int ServerStatus();
+    int GetCpuStatus();
+    int SetCpuStatus(int Status);
+    int ClientsCount();
+};
+typedef TS7Server *PS7Server;
+
+//******************************************************************************
+//                          PARTNER CLASS DEFINITION
+//******************************************************************************
+class TS7Partner
+{
+private:
+	S7Object Partner; // Partner Handle
+public:
+	TS7Partner(bool Active);
+	~TS7Partner();
+	// Control
+	int GetParam(int ParamNumber, void *pValue);
+	int SetParam(int ParamNumber, void *pValue);
+	int Start();
+	int StartTo(const char *LocalAddress,
+				const char *RemoteAddress,
+				int LocalTSAP,
+				int RemoteTSAP);
+	int Stop();
+	// 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);
+	// Properties
+	int Status();
+	int LastError();
+	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 // __cplusplus
+#endif // snap7_h

+ 203 - 0
examples/plain-c/srv_resourceless.c

@@ -0,0 +1,203 @@
+/*=============================================================================|
+|  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/                                   |
+|==============================================================================|
+|                                                                              |
+|  Server Example                                                              |
+|                                                                              |
+|=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "snap7.h"
+
+     S7Object Server;
+	 typedef byte TRWBuffer[1024];
+	 typedef byte *PRWBuffer;
+	 byte cnt = 0;
+
+//------------------------------------------------------------------------------
+// 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
+#endif
+	 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');
+			 }
+		 }
+	 }
+
+ //------------------------------------------------------------------------------
+ // Read/Write callback
+ //------------------------------------------------------------------------------
+	 int S7API RWAreaCallBack(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData)
+	 {
+		 PRWBuffer PBuffer = (PRWBuffer)pUsrData;
+		 int c;
+
+		 if (Operation == OperationRead)
+			 printf("Read Request\n");
+		 else
+			 printf("Write Request\n");
+
+		 switch (PTag->Area)
+		 {
+         //case S7AreaPE: printf("Area : PE, ");
+		 case 0x81: printf("Area : PE, ");
+			 break;
+		 case 0x82: printf("Area : PA, ");
+			 break;
+		 case 0x83: printf("Area : MK, ");
+			 break;
+		 case 0x1C: printf("Area : CT, ");
+			 break;
+		 case 0x1D: printf("Area : TM, ");
+			 break;
+		 case 0x84: printf("Area : DB%d, ", PTag->DBNumber);
+			 break;
+		 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);
+		 else
+		 {
+			 for (c = 0; c < 1024; c++)
+				 PBuffer[c] = cnt;
+			 cnt++;
+
+		 }
+		 printf("\n");
+		 return 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
+	char text[1024];
+	Srv_EventText(PEvent, text, 1024);
+    printf("%s\n",text);
+};
+
+int main(int argc, char* argv[])
+{
+    int Error;
+	char text[1024];
+
+	Server = Srv_Create();
+
+	// Filter a bit of noise
+	Srv_SetMask(Server, mkEvent, 0x3ff);
+	// Set the Read/Write callback 
+	Srv_SetRWAreaCallback(Server, RWAreaCallBack, NULL);
+	// Set the event callback to show something : it's not strictly needed.
+    // If you comment next line the server still works fine.
+    Srv_SetEventsCallback(Server, EventCallBack, NULL);
+
+    // Start the server onto the default adapter.
+    // To select an adapter we have to use Srv_StartTo(Server, "192.168.x.y").
+    // Start() is the same of StartTo("0.0.0.0");
+    Error=Srv_Start(Server);
+    if (Error==0)
+    {
+	// Now the server is running ... wait a key to terminate
+        getchar();
+    }
+    else
+	{
+        Srv_ErrorText(Error, text, 1024);
+        printf("%s\n", text);
+	}
+
+    // 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".
+
+    Srv_Stop(Server); // <- not strictly needed, every server is stopped on deletion
+                    //    and every client is gracefully disconnected.
+	Srv_Destroy(&Server);
+}
+
+// Finally, this is a very minimalist (but working) server :
+/*
+int main(int argc, char* argv[])
+{
+   TS7Server *Server = new TS7Server;
+   Server->Start();
+   getchar();
+   delete Server;
+}
+*/

+ 674 - 0
gpl.txt

@@ -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>.

+ 165 - 0
lgpl-3.0.txt

@@ -0,0 +1,165 @@
+                   GNU LESSER 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.
+
+
+  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
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   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.
+
+  Each version is given a distinguishing version number. If the
+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.

BIN
release/Linux/i586/libsnap7.so


+ 503 - 0
src/core/s7_client.cpp

@@ -0,0 +1,503 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "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;
+          else
+              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;
+    if (ThreadCreated)
+	{
+		CloseThread();
+		Disconnect();
+		OpenThread();
+	}
+	else
+		Disconnect();
+	
+	if (DoReconnect || WasConnected)
+       return Connect();
+    else
+       return 0;
+}
+//---------------------------------------------------------------------------
+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;
+    return 0;
+}
+//---------------------------------------------------------------------------
+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)
+{
+    // Actually there are no specific client params, maybe in future...
+	return TSnap7MicroClient::SetParam(ParamNumber, pValue);
+}
+//---------------------------------------------------------------------------
+bool TSnap7Client::CheckAsCompletion(int &opResult)
+{
+    if (!Job.Pending)
+        opResult=Job.Result;
+    else
+        if (!Destroying)
+            opResult=errCliJobPending; // don't set LastError here
+        else
+        {
+            opResult=errCliDestroying;
+            return true;
+        }
+
+    return !Job.Pending;
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+{
+     if (!Job.Pending)
+     {
+          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 0;
+     }
+     else
+          return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, void * pUsrData)
+{
+    int ByteSize, TotalSize;
+
+    if (!Job.Pending)
+    {
+        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();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsListBlocksOfType(int BlockType, PS7BlocksOfType pUsrData, int & ItemsCount)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opListBlocksOfType;
+        Job.Area     =BlockType;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&ItemsCount;
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsReadSZL(int ID, int Index, PS7SZL pUsrData, int & Size)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opReadSZL;
+        Job.ID       =ID;
+        Job.Index    =Index;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.IParam   =1; // Data has to be copied into user buffer
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsReadSZLList(PS7SZLList pUsrData, int &ItemsCount)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opReadSzlList;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&ItemsCount;
+        Job.Amount   =ItemsCount;
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+{
+    if (!Job.Pending)
+     {
+        Job.Pending  =true;
+        Job.Op       =s7opUpload;
+        Job.Area     =BlockType;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.Number   =BlockNum;
+        Job.IParam   =0; // not full upload, only data
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsFullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opUpload;
+        Job.Area     =BlockType;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.Number   =BlockNum;
+        Job.IParam   =1; // full upload
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsDownload(int BlockNum, void * pUsrData, int Size)
+{
+    if (!Job.Pending)
+    {
+        // 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.Pending  =true;
+        Job.Op       =s7opDownload;
+        // Doublebuffering
+        memcpy(&opData, pUsrData, Size);
+        Job.Number   =BlockNum;
+        Job.Amount   =Size;
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsCopyRamToRom(int Timeout)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opCopyRamToRom;
+        if (Timeout>0)
+        {
+          Job.IParam   =Timeout;
+          JobStart     =SysGetTick();
+          StartAsyncJob();
+          return 0;
+        }
+        else
+            return SetError(errCliInvalidParams);
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsCompress(int Timeout)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opCompress;
+        if (Timeout>0)
+        {
+            Job.IParam   =Timeout;
+            JobStart     =SysGetTick();
+            StartAsyncJob();
+            return 0;
+        }
+        else
+            return SetError(errCliInvalidParams);
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+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 (!Job.Pending)
+    {
+        if (Size<=0)
+            return SetError(errCliInvalidBlockSize);
+        Job.Pending  =true;
+        Job.Op       =s7opDBGet;
+        Job.Number   =DBNumber;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::AsDBFill(int DBNumber, int FillChar)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opDBFill;
+        Job.Number   =DBNumber;
+        Job.IParam   =FillChar;
+        JobStart     =SysGetTick();
+        StartAsyncJob();
+        return 0;
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+void TSnap7Client::StartAsyncJob()
+{
+    ClrError();
+	if (!ThreadCreated)
+	{
+		EvtJob =  new TSnapEvent(false);
+		EvtComplete = new TSnapEvent(false);
+	    OpenThread();
+		ThreadCreated=true;
+	}
+	EvtComplete->Reset(); // reset if previously was not called WaitAsCompletion
+    EvtJob->Set();
+}
+//---------------------------------------------------------------------------
+int TSnap7Client::WaitAsCompletion(unsigned long Timeout)
+{
+    if (Job.Pending)
+    {
+        if (ThreadCreated)
+		{
+			if (EvtComplete->WaitFor(Timeout)==WAIT_OBJECT_0)
+				return Job.Result;
+			else
+			{  
+				if (Destroying)
+					return errCliDestroying;
+				else
+					return SetError(errCliJobTimeout);
+			}
+		}
+		else
+			return SetError(errCliJobTimeout);
+    }
+    else
+        return Job.Result;
+}
+//---------------------------------------------------------------------------
+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();
+          }
+     };
+}
+

+ 104 - 0
src/core/s7_client.h

@@ -0,0 +1,104 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_client_h
+#define s7_client_h
+//---------------------------------------------------------------------------
+#include "snap_threads.h"
+#include "s7_micro_client.h"
+//---------------------------------------------------------------------------
+
+extern "C" {
+typedef void (S7API *pfn_CliCompletion) (void * usrPtr, int opCode, int opResult);
+}
+class TSnap7Client;
+
+class TClientThread: public TSnapThread
+{
+private:
+	TSnap7Client * FClient;
+public:
+     TClientThread(TSnap7Client *Client)
+     {
+           FClient = Client;
+     }
+	void Execute();
+};
+//---------------------------------------------------------------------------
+class TSnap7Client: public TSnap7MicroClient
+{
+private:
+    TClientThread *FThread;
+	bool ThreadCreated;
+    void CloseThread();
+    void OpenThread();
+    void StartAsyncJob();
+protected:
+    PSnapEvent EvtJob;
+    PSnapEvent EvtComplete;
+    pfn_CliCompletion CliCompletion;
+    void *FUsrPtr;
+    void DoCompletion();
+public:
+    friend class TClientThread;
+    TSnap7Client();
+    ~TSnap7Client();
+    int Reset(bool DoReconnect);
+    int SetAsCallback(pfn_CliCompletion pCompletion, void * usrPtr);
+    int GetParam(int ParamNumber, void *pValue);
+    int SetParam(int ParamNumber, void *pValue);
+    // Async functions
+    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 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 TSnap7Client *PSnap7Client;
+
+//---------------------------------------------------------------------------
+#endif // s7_client_h

+ 1256 - 0
src/core/s7_firmware.h

@@ -0,0 +1,1256 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#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,
+    0x00,0x00
+  };
+
+  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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x15,
+    0x00,0x00
+  };
+
+  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,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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,0x00,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,
+    0x00,0x00,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,
+    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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    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,
+    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,
+    0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04,
+    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,
+    0x33,0x31,0x35,0x2D,0x32,0x45,0x48,0x31,0x34,0x2D,0x30,0x41,0x42,0x30,0x20,0x00,0xC0,0x00,0x04,
+    0x00,0x01
+  };
+
+  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] = {
+    0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04
+  };
+
+  byte SZL_ID_0F11_IDX_0007[12] = {
+    0xFF,0x09,0x00,0x08,0x0F,0x11,0x00,0x00,0x00,0x1C,0x00,0x04
+  };
+
+  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] = {
+    0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17
+  };
+
+  byte SZL_ID_0F12_IDX_0200[12] = {
+    0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17
+  };
+
+  byte SZL_ID_0F12_IDX_0400[12] = {
+    0xFF,0x09,0x00,0x08,0x0F,0x12,0x00,0x00,0x00,0x02,0x00,0x17
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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,
+    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
+  };
+
+  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,
+    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,
+    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
+  };
+
+  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,
+    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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    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,
+    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,
+    0x00,0x00,0x00,0x00,0x00,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,
+    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
+  };
+
+  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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    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,0x00,0x00,0x00,0x00,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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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_0232_IDX_0004[52] = {
+    0xFF,0x09,0x00,0x30,0x02,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    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_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,
+    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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    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,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,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,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,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,0x52,0x16,0x38,0x86
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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
+  };
+
+  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,
+    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_01A0_IDX_000B[232] = {
+    0xFF,0x09,0x00,0xE4,0x01,0xA0,0x00,0x00,0x00,0x14,0x00,0x0B,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,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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,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,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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,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,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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,
+    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,
+    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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,
+    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,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,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,0x43,0x01,0xFF,0x46,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,0x02,0x04,0x23,
+    0x50,0x45,0x00,0x96,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x02,0x04,
+    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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,0x14,0x77,0x14,0x94,
+    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,0x43,0x04,0xFF,0x84,0x00,0x00,0x00,0x00,0x00,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,0x13,0x81,0xFE,0x64,0xC7,0x72,0x43,0x04,0x08,
+    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,0x43,0x04,0xFF,0x84,0x00,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,
+    0x00,0x00,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,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,
+    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,
+    0x00,0x00,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
+  };
+
+
+#endif
+

+ 541 - 0
src/core/s7_isotcp.cpp

@@ -0,0 +1,541 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "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;
+	int Size;
+    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);
+		else
+		  return noError;
+	}
+	else
+		return SetIsoError(errIsoNullPointer);
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::SetIsoError(int Error)
+{
+	LastIsoError = Error | LastTcpError;
+	return LastIsoError;
+}
+//---------------------------------------------------------------------------
+void TIsoTcpSocket::ClrIsoError()
+{
+    LastIsoError=0;
+    LastTcpError=0;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::BuildControlPDU()
+{
+	int ParLen, IsoLen;
+
+    ClrIsoError();
+	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;
+			break;
+		case 256:
+			FControlPDU.COTP.Params.PduSizeVal =0x08;
+			break;
+		case 512:
+			FControlPDU.COTP.Params.PduSizeVal =0x09;
+			break;
+		case 1024:
+			FControlPDU.COTP.Params.PduSizeVal =0x0A;
+			break;
+		case 2048:
+			FControlPDU.COTP.Params.PduSizeVal =0x0B;
+			break;
+		case 4096:
+			FControlPDU.COTP.Params.PduSizeVal =0x0C;
+			break;
+		case 8192:
+			FControlPDU.COTP.Params.PduSizeVal =0x0D;
+			break;
+		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)      +
+						  // 2 Src TSAP 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 !!!
+	return noError;
+}
+//---------------------------------------------------------------------------
+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;
+
+	ClrIsoError();
+	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;
+	int Result;
+
+	// Build the default connection telegram
+	BuildControlPDU();
+    ControlPDU =&FControlPDU;
+
+	// Checks the format
+	Result =CheckPDU(ControlPDU, pdu_type_CR);
+	if (Result!=0)
+		return Result;
+
+	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));
+			if (LastTcpError==0)
+			{
+				// 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);
+					if (LastTcpError==0)
+					{
+						// Finally checks the Connection Confirm telegram
+						Result =CheckPDU(ControlPDU, pdu_type_CC);
+						if (Result!=0)
+							LastIsoError=Result;
+					}
+					else
+						Result =SetIsoError(errIsoRecvPacket);
+				}
+				else
+					Result =SetIsoError(errIsoInvalidPDU);
+			}
+			else
+				Result =SetIsoError(errIsoRecvPacket);
+			// Flush buffer
+			if (Result!=0)
+				Purge();
+		}
+		else
+			Result =SetIsoError(errIsoSendPacket);
+
+		if (Result!=0)
+			SckDisconnect();
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoSendBuffer(void *Data, int Size)
+{
+	int Result;
+	u_int IsoSize;
+
+    ClrIsoError();
+	// 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(errIsoSendPacket);
+	}
+	else
+		Result =SetIsoError(errIsoInvalidDataSize );
+	return Result;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoRecvBuffer(void *Data, int & Size)
+{
+	int Result;
+
+    ClrIsoError();
+	Size =0;
+	Result =isoRecvPDU(&PDU);
+	if (Result==0)
+	{
+		Size =PDUSize( &PDU )-DataHeaderSize;
+		if (Data!=0)  // Data=NULL ==> a child will consume directly PDY.Payload
+            memcpy(Data, &PDU.Payload, Size);
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoExchangeBuffer(void *Data, int &Size)
+{
+	int Result;
+
+    ClrIsoError();
+	Result =isoSendBuffer(Data, Size);
+	if (Result==0)
+		Result =isoRecvBuffer(Data, Size);
+	return Result;
+}
+//---------------------------------------------------------------------------
+bool TIsoTcpSocket::IsoPDUReady()
+{
+    ClrIsoError();
+	return PacketReady(sizeof(TCOTP_DT));
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoDisconnect(bool OnlyTCP)
+{
+	int Result;
+
+    ClrIsoError();
+	if (Connected)
+		Purge(); // Flush pending
+	LastIsoError=0;
+	// OnlyTCP true -> Disconnect Request telegram is not required : only TCP disconnection
+	if (!OnlyTCP)
+	{
+		// if we are connected -> we have a valid connection telegram
+		if (Connected)
+			FControlPDU.COTP.PDUType =pdu_type_DR;
+		// Checks the format
+		Result =CheckPDU(&FControlPDU, pdu_type_DR);
+		if (Result!=0)
+			return Result;
+		// Sends Disconnect request
+		SendPacket(&FControlPDU, PDUSize(&FControlPDU));
+		if (LastTcpError!=0)
+		{
+			Result =SetIsoError(errIsoSendPacket);
+			return Result;
+		}
+	}
+	// TCP disconnect
+	SckDisconnect();
+	if (LastTcpError!=0)
+		Result =SetIsoError(errIsoDisconnect);
+	else
+		Result =0;
+
+	return Result;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoSendPDU(PIsoDataPDU Data)
+{
+	int Result;
+
+    ClrIsoError();
+	Result=CheckPDU(Data,pdu_type_DT);
+	if (Result==0)
+	{
+		SendPacket(Data,PDUSize(Data));
+		if (LastTcpError!=0)
+			Result=SetIsoError(errIsoSendPacket);
+	}
+    return Result;
+}
+//------------------------------------------------------------------------------
+int TIsoTcpSocket::isoRecvFragment(void *From, int Max, int &Size, bool &EoT)
+{
+	int DataLength;
+
+	Size =0;
+	EoT =false;
+    byte PDUType;
+    ClrIsoError();
+	// header is received always from beginning
+	RecvPacket(&PDU, DataHeaderSize); // TPKT + COPT_DT
+	if (LastTcpError==0)
+	{
+        PDUType=PDU.COTP.PDUType;
+        switch (PDUType)
+        {
+			case pdu_type_CR:
+			case pdu_type_DR:
+				EoT=true;
+				break;
+			case pdu_type_DT:
+                EoT = (PDU.COTP.EoT_Num & 0x80) == 0x80;  // EoT flag
+				break;
+			default:
+				return SetIsoError(errIsoInvalidPDU);
+        }
+
+		DataLength = PDUSize(&PDU) - DataHeaderSize;
+		if (CheckPDU(&PDU, PDUType)!=0)
+			return LastIsoError;
+		// Checks for data presence
+		if (DataLength>0)  // payload present
+		{
+			// Check if the data fits in the buffer
+			if(DataLength<=Max)
+			{
+				RecvPacket(From, DataLength);
+				if (LastTcpError!=0)
+					return SetIsoError(errIsoRecvPacket);
+				else
+					Size =DataLength;
+			}
+			else
+				return SetIsoError(errIsoPduOverflow);
+		}
+	}
+	else
+		return SetIsoError(errIsoRecvPacket);
+
+	return LastIsoError;
+}
+//---------------------------------------------------------------------------
+// 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)
+{
+	int Result;
+	int Size;
+	pbyte pData;
+	int max;
+	int Offset;
+	int Received;
+	int NumParts;
+	bool Complete;
+
+	NumParts =1;
+	Offset =0;
+	Complete =false;
+    ClrIsoError();
+	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);
+			}
+		}
+		else
+			Result =SetIsoError(errIsoTooManyFragments);
+	} while ((!Complete) && (Result==0));
+
+
+	if (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);
+	}
+	else
+        if (LastTcpError!=WSAECONNRESET)
+            Purge();
+	return Result;
+}
+//---------------------------------------------------------------------------
+int TIsoTcpSocket::isoExchangePDU(PIsoDataPDU Data)
+{
+    int Result;
+    ClrIsoError();
+	Result=isoSendPDU(Data);
+	if (Result==0)
+		Result=isoRecvPDU(Data);
+	return Result;
+}
+//---------------------------------------------------------------------------
+void TIsoTcpSocket::IsoPeek(void *pPDU, TPDUKind &PduKind)
+{
+	PIsoHeaderInfo Info;
+	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;
+        return;
+    };
+    // Here IsoLen>DataHeaderSize : check the PDUType
+    switch (Info->PDUType)
+    {
+        case pdu_type_CR:
+            PduKind=pkConnectionRequest;
+            break;
+        case pdu_type_DR:
+            PduKind=pkDisconnectRequest;
+            break;
+        case pdu_type_DT:
+            PduKind=pkValidData;
+            break;
+        default:
+            PduKind=pkUnrecognizedType;
+    };
+}
+
+
+

+ 271 - 0
src/core/s7_isotcp.h

@@ -0,0 +1,271 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_isotcp_h
+#define s7_isotcp_h
+//---------------------------------------------------------------------------
+#include "snap_msgsock.h"
+//---------------------------------------------------------------------------
+#pragma pack(1)
+
+#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 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
+
+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)
+typedef struct{
+	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
+
+typedef struct {
+	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
+typedef struct {
+	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
+typedef struct {
+	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).
+typedef struct {
+	TTPKT TPKT; 	// TPKT Header
+			// Common part of any COTP
+	u_char HLength; // Header length : 3 for this header
+	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;
+
+typedef struct {
+	TTPKT 	 TPKT; // TPKT Header
+	TCOTP_CO COTP; // COPT Header for CONNECTION stuffs
+} TIsoControlPDU;
+typedef TIsoControlPDU *PIsoControlPDU;
+
+typedef u_char TIsoPayload[IsoPayload_Size];
+
+typedef struct {
+	TTPKT 	    TPKT; // TPKT Header
+	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 ;
+
+#pragma pack()
+
+void ErrIsoText(int Error, char *Msg, int len);
+
+class TIsoTcpSocket : public TMsgSocket
+{
+private:
+
+	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);
+protected:
+	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);
+public:
+	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

+ 3328 - 0
src/core/s7_micro_client.cpp

@@ -0,0 +1,3328 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "s7_micro_client.h"
+//---------------------------------------------------------------------------
+
+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 Start;
+     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
+     int Size;
+     int Result;
+
+     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;
+     Offset     =0;
+     Result     =0;
+     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);
+          else
+               ReqParams->Items[0].DBNumber = 0x0000;
+          // Adjusts the offset
+          if ((Job.WordLen==S7WLBit) || (Job.WordLen==S7WLCounter) || (Job.WordLen==S7WLTimer))
+               Address = Start;
+          else
+               Address = Start*8;
+
+          ReqParams->Items[0].Address[2] = Address & 0x000000FF;
+          Address = Address >> 8;
+          ReqParams->Items[0].Address[1] = Address & 0x000000FF;
+          Address = Address >> 8;
+          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);
+                }
+                else
+                     Result = CpuError(ResData->ReturnCode);
+           Offset+=Size;
+          };
+          //--------------------------------------------------------------------
+          TotElements -= NumElements;
+          Start += NumElements*WordSize;
+     }
+     return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opWriteArea()
+{
+     PReqFunWriteParams   ReqParams;
+     PReqFunWriteDataItem ReqData;  // only 1 item for WriteArea Function
+     PResFunWrite         ResParams;
+     PS7ResHeader23       Answer;
+     word RPSize;  // ReqParams size
+     word RHSize;  // Request headers size
+     bool First = true;
+     pbyte Source;
+     pbyte Target;
+     int Address;
+     int IsoSize;
+     int WordSize;
+     word Size;
+     uintptr_t Offset = 0;
+     int Start;        // where we are starting from for this telegram
+     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
+     int Result = 0;
+
+     WordSize=DataSizeByte(Job.WordLen); // The size in bytes of an element that we are pushing
+     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;
+
+     RHSize =sizeof(TS7ReqHeader)+    // Request header
+             2+                       // FunWrite+ItemCount (of TReqFunWriteParams)
+             sizeof(TReqFunWriteItem)+// 1 item reference
+             4;                       // ReturnCode+TransportSize+DataLength
+     RPSize =sizeof(TReqFunWriteItem)+2;
+
+     // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+     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
+     Answer   =PS7ResHeader23(&PDU.Payload);
+     ResParams=PResFunWrite(pbyte(Answer)+ResHeaderSize23);
+
+     // 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-RHSize) / WordSize;
+     TotElements=Job.Amount;
+     Start      =Job.Start;
+     while ((TotElements>0) && (Result==0))
+     {
+           NumElements=TotElements;
+           if (NumElements>MaxElements)
+               NumElements=MaxElements;
+           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;
+           if (Job.Area==S7AreaDB)
+               ReqParams->Items[0].DBNumber=SwapWord(Job.Number);
+           else
+               ReqParams->Items[0].DBNumber=0x0000;
+
+           // Adjusts the offset
+           if ((Job.WordLen==S7WLBit) || (Job.WordLen==S7WLCounter) || (Job.WordLen==S7WLTimer))
+               Address=Start;
+           else
+               Address=Start*8;
+
+           ReqParams->Items[0].Address[2]=Address & 0x000000FF;
+           Address=Address >> 8;
+           ReqParams->Items[0].Address[1]=Address & 0x000000FF;
+           Address=Address >> 8;
+           ReqParams->Items[0].Address[0]=Address & 0x000000FF;
+
+           ReqData->ReturnCode=0x00;
+
+           switch(Job.WordLen)
+           {
+               case S7WLBit:
+                   ReqData->TransportSize=TS_ResBit;
+                   break;
+               case S7WLInt:
+               case S7WLDInt:
+                   ReqData->TransportSize=TS_ResInt;
+                   break;
+               case S7WLReal:
+                   ReqData->TransportSize=TS_ResReal;
+                   break;
+               case S7WLChar   :
+               case S7WLCounter:
+               case S7WLTimer:
+                   ReqData->TransportSize=TS_ResOctet;
+				   break;
+			   default:
+                   ReqData->TransportSize=TS_ResByte;
+                   break;
+           };
+
+           if ((ReqData->TransportSize!=TS_ResOctet) && (ReqData->TransportSize!=TS_ResReal) && (ReqData->TransportSize!=TS_ResBit))
+               ReqData->DataLength=SwapWord(Size*8);
+           else
+               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
+               if (Result==0)
+               {     // 2th check : item error
+                   if (ResParams->Data[0] == 0xFF) // <-- 0xFF means Result OK
+                       Result=0;
+                   else
+                       // 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]);
+                       else
+                           Result=errCliPartialDataWritten;
+               };
+               Offset+=Size;
+           };
+           First=false;
+
+           TotElements-=NumElements;
+           Start+=(NumElements*WordSize);
+     }
+     return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opReadMultiVars()
+{
+    PS7DataItem       Item;
+    PReqFunReadParams ReqParams;
+    PS7ResHeader23    Answer;
+    PResFunReadParams ResParams;
+    TResFunReadData   ResData;
+
+    word       RPSize; // ReqParams size
+    uintptr_t  Offset =0 ;
+    word       Slice;
+    longword   Address;
+    int        IsoSize;
+    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->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);   // Request params size
+    PDUH_out->DataLen=0x0000;            // No data in output
+
+    // Fill Params
+    ReqParams->FunRead=pduFuncRead;      // 0x04
+    ReqParams->ItemsCount=ItemsCount;
+
+    Item = PS7DataItem(Job.pData);
+    for (c = 0; c < ItemsCount; c++)
+    {
+        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);
+        else
+    	    ReqParams->Items[c].DBNumber=0x0000;
+        // Adjusts the offset
+        if ((Item->WordLen==S7WLBit) || (Item->WordLen==S7WLCounter) || (Item->WordLen==S7WLTimer))
+        	Address=Item->Start;
+        else
+        	Address=Item->Start*8;
+        // Builds the offset
+        ReqParams->Items[c].Address[2]=Address & 0x000000FF;
+        Address=Address >> 8;
+        ReqParams->Items[c].Address[1]=Address & 0x000000FF;
+        Address=Address >> 8;
+        ReqParams->Items[c].Address[0]=Address & 0x000000FF;
+        Item++;
+    };
+
+    IsoSize=RPSize+sizeof(TS7ReqHeader);
+	if (IsoSize>PDULength) 
+		return errCliSizeOverPDU;
+	Result=isoExchangeBuffer(0,IsoSize);
+
+	if (Result!=0)
+        return Result;
+
+    // Function level error
+    if (Answer->Error!=0)
+    	return CpuError(SwapWord(Answer->Error));
+
+    if (ResParams->ItemCount!=ItemsCount)
+    	return errCliInvalidPlcAnswer;
+
+    P=pbyte(ResParams)+sizeof(TResFunReadParams);
+    Item = PS7DataItem(Job.pData);
+    for (c = 0; c < ItemsCount; c++)
+    {
+        ResData[c] =PResFunReadItem(pbyte(P)+Offset);
+        Slice=0;
+        // Item level error
+        if (ResData[c]->ReturnCode==0xFF) // <-- 0xFF means Result OK
+        {
+          // Calcs data size in bytes
+          Slice=SwapWord(ResData[c]->DataLength);
+          // Adjust Size in accord of TransportSize
+          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=0;
+        }
+        else
+          Item->Result=CpuError(ResData[c]->ReturnCode);
+
+        if ((Slice % 2)!=0)
+        	Slice++; // Skip fill byte for Odd frame
+
+        Offset+=(4+Slice);
+        Item++;
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opWriteMultiVars()
+{
+    PS7DataItem        Item;
+    PReqFunWriteParams ReqParams;
+    PResFunWrite       ResParams;
+    TReqFunWriteData   ReqData;
+    PS7ResHeader23     Answer;
+    pbyte              P;
+    uintptr_t          Offset;
+    longword           Address;
+    int                ItemsCount, c, IsoSize;
+    word               RPSize; // ReqParams size
+    word               Size;   // Write data size
+    int                WordSize, 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 : setup pointers
+    RPSize    = word(2 + ItemsCount * sizeof(TReqFunWriteItem));
+    ReqParams = PReqFunWriteParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer    = PS7ResHeader23(&PDU.Payload);
+    ResParams = PResFunWrite(pbyte(Answer)+ResHeaderSize23);
+    P=pbyte(ReqParams)+RPSize;
+
+    // Fill Header
+    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);   // Request params size
+
+    // Fill Params
+    ReqParams->FunWrite=pduFuncWrite;      // 0x05
+    ReqParams->ItemsCount=ItemsCount;
+
+    Offset=0;
+    Item  = PS7DataItem(Job.pData);
+    for (c = 0; c < ItemsCount; c++)
+    {
+        // Items Params
+        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;
+
+        if (Item->Area==S7AreaDB)
+            ReqParams->Items[c].DBNumber=SwapWord(Item->DBNumber);
+        else
+            ReqParams->Items[c].DBNumber=0x0000;
+
+        // Adjusts the offset
+        if ((Item->WordLen==S7WLBit) || (Item->WordLen==S7WLCounter) || (Item->WordLen==S7WLTimer))
+        	Address=Item->Start;
+        else
+        	Address=Item->Start*8;
+        // Builds the offset
+        ReqParams->Items[c].Address[2]=Address & 0x000000FF;
+        Address=Address >> 8;
+        ReqParams->Items[c].Address[1]=Address & 0x000000FF;
+        Address=Address >> 8;
+        ReqParams->Items[c].Address[0]=Address & 0x000000FF;
+
+        // Items Data
+        ReqData[c]=PReqFunWriteDataItem(pbyte(P)+Offset);
+        ReqData[c]->ReturnCode=0x00;
+
+        switch (Item->WordLen)
+        {
+          case S7WLBit     :
+               ReqData[c]->TransportSize=TS_ResBit;
+               break;
+		  case S7WLInt     :
+          case S7WLDInt    :
+               ReqData[c]->TransportSize=TS_ResInt;
+               break;
+          case S7WLReal    :
+               ReqData[c]->TransportSize=TS_ResReal;
+               break;
+          case S7WLChar    :
+          case S7WLCounter :
+          case S7WLTimer   : ReqData[c]->TransportSize=TS_ResOctet;
+			   break;
+		  default :
+			   ReqData[c]->TransportSize=TS_ResByte; // byte/word/dword etc.
+			   break;
+        };
+
+        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);
+        else
+           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
+        Item++;
+    };
+
+    PDUH_out->DataLen=SwapWord(word(Offset));
+
+    IsoSize=RPSize+sizeof(TS7ReqHeader)+int(Offset);
+	if (IsoSize>PDULength) 
+		return errCliSizeOverPDU;
+    Result=isoExchangeBuffer(0,IsoSize);
+
+	if (Result!=0)
+		return Result;
+
+	// Function level error
+	if (Answer->Error!=0)
+		return CpuError(SwapWord(Answer->Error));
+
+    if (ResParams->ItemCount!=ItemsCount)
+        return errCliInvalidPlcAnswer;
+
+    Item  = PS7DataItem(Job.pData);
+    for (c = 0; c < ItemsCount; c++)
+    {
+        // Item level error
+        if (ResParams->Data[c]==0xFF) // <-- 0xFF means Result OK
+           Item->Result=0;
+        else
+           Item->Result=CpuError(ResParams->Data[c]);
+        Item++;
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opListBlocks()
+{
+    PReqFunGetBlockInfo ReqParams;
+    PReqDataFunBlocks   ReqData;
+    PResFunGetBlockInfo ResParams;
+    PDataFunListAll     ResData;
+    PS7ResHeader17      Answer;
+    PS7BlocksList       List;
+    int IsoSize, Result;
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    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);
+    // Fill Header
+    PDUH_out->P=0x32;                    // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;  // 0x07
+    PDUH_out->AB_EX=0x0000;              // Always 0x0000
+    PDUH_out->Sequence=GetNextWord();    // AutoInc
+    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);
+    Result=isoExchangeBuffer(0,IsoSize);
+    // Get Data
+    if (Result==0)
+    {
+        if (ResParams->ErrNo==0)
+        {
+            if (SwapWord(ResData->Length)!=28)
+              return errCliInvalidPlcAnswer;
+
+            for (int c = 0; c < 7; c++)
+            {
+                switch (ResData->Blocks[c].BType)
+                {
+                  case Block_OB:
+                      List->OBCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_DB:
+                      List->DBCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_SDB:
+                      List->SDBCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_FC:
+                      List->FCCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_SFC:
+                      List->SFCCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_FB:
+                      List->FBCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                  case Block_SFB:
+                      List->SFBCount=SwapWord(ResData->Blocks[c].BCount);
+                      break;
+                }
+            }
+        }
+        else
+          Result=CpuError(SwapWord(ResParams->ErrNo));
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opListBlocksOfType()
+{
+    PReqFunGetBlockInfo ReqParams;
+    PReqDataBlockOfType ReqData;
+
+    PS7ResHeader17      Answer;
+    PResFunGetBlockInfo ResParams;
+    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);
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader17(&PDU.Payload);
+    ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17);
+    ResData  =PDataFunGetBot(pbyte(ResParams)+sizeof(TResFunGetBlockInfo));
+    // Get Data
+    First =true;
+    In_Seq=0x00; // first group sequence, next will come from PLC
+    Count =0;
+    Last  =0;
+    do
+    {
+    //<--------------------------------------------------------- Get next slice
+        // Fill Header
+        PDUH_out->P=0x32;                    // Always 0x32
+        PDUH_out->PDUType=PduType_userdata;  // 0x07
+        PDUH_out->AB_EX=0x0000;              // Always 0x0000
+        PDUH_out->Sequence=GetNextWord();    // AutoInc
+        if (First)
+        {
+           PDUH_out->ParLen=SwapWord(8);     // 8 bytes params
+           PDUH_out->DataLen=SwapWord(6);    // 6 bytes data
+           DataLength=14;
+        }
+        else
+        {
+           PDUH_out->ParLen=SwapWord(12);    // 12 bytes params
+           PDUH_out->DataLen=SwapWord(4);    // 4 bytes data
+           DataLength=16;
+        }
+
+        // Fill params (mostly constants)
+        ReqParams->Head[0]=0x00;
+        ReqParams->Head[1]=0x01;
+        ReqParams->Head[2]=0x12;
+
+        if (First)
+            ReqParams->Plen   =0x04;
+		else
+            ReqParams->Plen   =0x08;
+
+		if (First)
+			ReqParams->Uk = 0x11;
+		else
+			ReqParams->Uk = 0x12;
+
+        ReqParams->Tg     =grBlocksInfo;
+        ReqParams->SubFun =SFun_ListBoT;
+        ReqParams->Seq    =In_Seq;
+
+        // Fill data
+        if (First)
+        {
+            // 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;
+        }
+        else
+        {
+            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;
+        Result=isoExchangeBuffer(0,IsoSize);
+
+        if (Result==0)
+        {
+            if (ResParams->ErrNo==0)
+            {
+                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;
+                             break;
+                         };
+                     };
+                     Count+=CThis; // Total counter
+                     List--;
+                }
+                else
+                    Result=errCliItemNotAvailable;
+            }
+            else
+                Result=errCliItemNotAvailable;
+        };
+        First=false;
+    //---------------------------------------------------------> Get next slice
+    }
+    while ((!Done) && (Result==0));
+
+	*Job.pAmount=0;
+	if (Result==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;
+	};
+
+	return Result;
+}
+//---------------------------------------------------------------------------
+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);
+    }
+    else
+        *PTime='\0';
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opAgBlockInfo()
+{
+    PS7BlockInfo BlockInfo;
+    PReqFunGetBlockInfo ReqParams;
+    PReqDataBlockInfo ReqData;
+    PS7ResHeader17 Answer;
+    PResFunGetBlockInfo ResParams;
+    PResDataBlockInfo ResData;
+    byte BlockType;
+    int BlockNum, IsoSize, Result;
+
+    BlockType=Job.Area;
+    BlockNum =Job.Number;
+    BlockInfo=PS7BlockInfo(Job.pData);
+    memset(BlockInfo,0,sizeof(TS7BlockInfo));
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunGetBlockInfo(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ReqData  =PReqDataBlockInfo(pbyte(ReqParams)+sizeof(TReqFunGetBlockInfo));
+    Answer   =PS7ResHeader17(&PDU.Payload);
+    ResParams=PResFunGetBlockInfo(pbyte(Answer)+ResHeaderSize17);
+    ResData  =PResDataBlockInfo(pbyte(ResParams)+sizeof(TResFunGetBlockInfo));
+    // Fill Header
+    PDUH_out->P=0x32;                    // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;  // 0x07
+    PDUH_out->AB_EX=0x0000;              // Always 0x0000
+	PDUH_out->Sequence=GetNextWord();    // AutoInc
+    PDUH_out->ParLen=SwapWord(sizeof(TReqFunGetBlockInfo));   // 8 bytes params
+    PDUH_out->DataLen=SwapWord(sizeof(TReqDataBlockInfo));   // 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_BlkInfo;
+    ReqParams->Seq    =0x00;
+    // Fill data
+    ReqData->RetVal   =0xFF;
+    ReqData->TSize    =TS_ResOctet;
+    ReqData->DataLen  =SwapWord(0x0008);
+    ReqData->BlkPrfx  =0x30;
+    ReqData->BlkType  =BlockType;
+    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);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    // Get Data
+    if (Result==0)
+    {
+        if (ResParams->ErrNo==0)
+        {
+            if (SwapWord(ResData->Length)<40) // 78
+                return errCliInvalidPlcAnswer;
+            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
+            }
+            else
+                Result=CpuError(ResData->RetVal);
+        }
+        else
+            Result=CpuError(SwapWord(ResParams->ErrNo));
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opDBGet()
+{
+    TS7BlockInfo BI;
+    void * usrPData;
+    int * usrPSize;
+    int Result, Room;
+    bool RoomError = false;
+
+    // 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.
+    if (Result==0)
+    {
+        // Check user space
+        if (BI.MC7Size>Room)
+        {
+           Job.Amount=Room;
+           RoomError=true;
+        }
+        else
+            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();
+        if (Result==0)
+           *usrPSize=Job.Amount;
+    }
+
+    if ((Result==0) && RoomError)
+        return errCliBufferTooSmall;
+    else
+        return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opDBFill()
+{
+    TS7BlockInfo BI;
+    int Result;
+    // new op : get block info
+    Job.Op   =s7opAgBlockInfo;
+    Job.Area =Block_DB;
+    Job.pData=&BI;
+    Result   =opAgBlockInfo();
+    // Restore original op
+    Job.Op   =s7opDBFill;
+    // Fill internal buffer then write it
+    if (Result==0)
+    {
+        Job.Amount =BI.MC7Size;
+        Job.Area   =S7AreaDB;
+        Job.WordLen=S7WLByte;
+        Job.Start  =0;
+        memset(&opData, byte(Job.IParam), Job.Amount);
+        Job.pData  =&opData;
+        Result     =opWriteArea();
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opUpload()
+{
+    PS7ResHeader23 Answer;
+    int  IsoSize;
+    byte Upload_ID = 0; // not strictly needed, only to avoid warning
+	byte BlockType;
+    int  BlockNum, BlockLength, Result;
+    bool Done, Full; // if full==true, the data will be compatible to full download function
+    uintptr_t Offset;
+    bool RoomError = false;
+
+    BlockType=Job.Area;
+    BlockNum =Job.Number;
+    Full     =Job.IParam==1;
+    // Setup Answer (is the same for all Upload pdus)
+    Answer=  PS7ResHeader23(&PDU.Payload);
+    // Init sequence
+    Done  =false;
+    Offset=0;
+    //<-------------------------------------------------------------StartUpload
+    PReqFunStartUploadParams ReqParams;
+    PResFunStartUploadParams ResParams;
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunStartUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ResParams=PResFunStartUploadParams(pbyte(Answer)+ResHeaderSize23);
+    // Init Header
+    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(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;
+    BlockNum=BlockNum % 10000;
+    ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+    BlockNum=BlockNum % 1000;
+    ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30;
+    BlockNum=BlockNum % 100;
+    ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30;
+    BlockNum=BlockNum % 10;
+    ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30;
+    ReqParams->A=0x41; // 'A'
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunStartUploadParams);
+    Result=isoExchangeBuffer(0,IsoSize);
+    // Get Upload Infos (only ID now)
+    if (Result==0)
+    {
+      if (Answer->Error==0)
+        Upload_ID=ResParams->Upload_ID;
+      else
+        Result=CpuError(SwapWord(Answer->Error));
+    };
+    //------------------------------------------------------------->StartUpload
+    if (Result==0)
+    {
+         //<--------------------------------------------------------FirstUpload
+        PReqFunUploadParams ReqParams;
+        PResFunUploadParams ResParams;
+        PResFunUploadDataHeaderFirst ResDataHeader;
+        pbyte Source;
+        pbyte Target;
+        int Size;
+
+        // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+        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
+        else
+          Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderFirst);  // not full : skip the data header
+        // Init Header
+        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(sizeof(TReqFunUploadParams));// params size
+        PDUH_out->DataLen=0x0000;           // No data
+        // Init Params
+        ReqParams->FunUpld=pduUpload;
+        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
+
+        IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunUploadParams);
+        Result=isoExchangeBuffer(0,IsoSize);
+        // Get Upload Infos (only ID now)
+        if (Result==0)
+        {
+          if (Answer->Error==0)
+          {
+            Done=ResParams->EoU==0;
+            if (Full)
+                Size=SwapWord(Answer->DataLen)-4; // Full data Size
+            else
+                Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderFirst); // Size of this data slice
+
+            BlockLength=SwapWord(ResDataHeader->MC7Len); // Full block size in byte
+            Target=pbyte(&opData)+Offset;
+            memcpy(Target, Source, Size);
+            Offset+=Size;
+          }
+          else
+              Result=errCliUploadSequenceFailed;
+        };
+         //-------------------------------------------------------->FirstUpload
+         while (!Done && (Result==0))
+         {
+             //<----------------------------------------------------NextUpload
+            PReqFunUploadParams ReqParams;
+            PResFunUploadParams ResParams;
+            PResFunUploadDataHeaderNext ResDataHeader;
+            pbyte Source;
+            pbyte Target;
+            int Size;
+
+            // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+            ReqParams=PReqFunUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+            // Next upload pdu consists of params, small info header, data.
+            ResParams=PResFunUploadParams(pbyte(Answer)+ResHeaderSize23);
+            ResDataHeader=PResFunUploadDataHeaderNext(pbyte(ResParams)+sizeof(TResFunUploadParams));
+            Source=pbyte(ResDataHeader)+sizeof(TResFunUploadDataHeaderNext);
+            // Init Header
+            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(sizeof(TReqFunUploadParams));// params size
+            PDUH_out->DataLen=0x0000;           // No data
+            // Init Params
+            ReqParams->FunUpld=pduUpload;
+            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
+
+            IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunUploadParams);
+            Result=isoExchangeBuffer(0,IsoSize);
+            // Get Upload Infos (only ID now)
+            if (Result==0)
+            {
+                if (Answer->Error==0)
+                {
+                    Done=ResParams->EoU==0;
+                    Size=SwapWord(Answer->DataLen)-sizeof(TResFunUploadDataHeaderNext); // Size of this data slice
+                    Target=pbyte(&opData)+Offset;
+                    memcpy(Target, Source, Size);
+                    Offset+=Size;
+                }
+                else
+                    Result=errCliUploadSequenceFailed;
+            };
+             //---------------------------------------------------->NextUpload
+         }
+         if (Result==0)
+         {
+            //<----------------------------------------------------EndUpload;
+            PReqFunEndUploadParams ReqParams;
+            PResFunEndUploadParams ResParams;
+
+            // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+            ReqParams=PReqFunEndUploadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+            ResParams=PResFunEndUploadParams(pbyte(Answer)+ResHeaderSize23);
+            // Init Header
+            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(sizeof(TReqFunEndUploadParams));// params size
+            PDUH_out->DataLen=0x0000;           // No data
+            // Init Params
+            ReqParams->FunEUpld=pduEndUpload;
+            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
+
+            IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunEndUploadParams);
+            Result=isoExchangeBuffer(0,IsoSize);
+
+            // Get EndUpload Result
+            if (Result==0)
+            {
+               if ((Answer->Error!=0) || (ResParams->FunEUpld!=pduEndUpload))
+                   Result=errCliUploadSequenceFailed;
+            };
+            //---------------------------------------------------->EndUpload;
+         }
+    };
+
+    *Job.pAmount=0;
+    if (Result==0)
+    {
+        if (Full)
+        {
+            opSize=int(Offset);
+            if (opSize<78)
+                Result=errCliInvalidDataSizeRecvd;
+        }
+        else
+        {
+            opSize=BlockLength;
+            if (opSize<1)
+                Result=errCliInvalidDataSizeRecvd;
+        };
+        if (Result==0)
+        {
+			 // Checks user space
+             if (Job.Amount<opSize) {
+                 opSize=Job.Amount;
+				 RoomError = true;
+			 };
+			 memcpy(Job.pData, &opData, opSize);
+			 *Job.pAmount=opSize;
+			 if (RoomError) // Result==0 -> override if romerror
+				Result=errCliPartialDataRead;
+		};
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opDownload()
+{
+    PS7CompactBlockInfo Info;
+    PS7BlockFooter Footer;
+    int BlockNum, StoreBlockNum, BlockAmount;
+    int BlockSize, BlockSizeLd;
+    int BlockType, Remainder;
+    int Result, IsoSize;
+    bool Done  = false;
+    uintptr_t Offset;
+
+    BlockAmount=Job.Amount;
+    BlockNum   =Job.Number;
+    Result=CheckBlock(-1,-1,&opData,BlockAmount);
+    if (Result==0)
+    {
+        Info=PS7CompactBlockInfo(&opData);
+        // Gets blocktype
+        BlockType=SubBlockToBlock(Info->SubBlkType);
+
+        if (BlockNum>=0)
+            Info->BlkNum=SwapWord(BlockNum); // change the number
+        else
+            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;
+
+        Offset=0;
+        Remainder=BlockAmount;
+        //<---------------------------------------------- Start Download request
+            PReqStartDownloadParams ReqParams;
+            PResStartDownloadParams ResParams;
+            PS7ResHeader23          Answer;
+
+            // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+            ReqParams=PReqStartDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+            Answer   =PS7ResHeader23(&PDU.Payload);
+            ResParams=PResStartDownloadParams(pbyte(Answer)+ResHeaderSize23);
+            // Init Header
+            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(sizeof(TReqStartDownloadParams));
+            PDUH_out->DataLen=0x0000;             // No data
+            // Init Params
+            ReqParams->FunSDwnld = pduReqDownload;
+            ReqParams->Uk6[0]=0x00;
+            ReqParams->Uk6[1]=0x01;
+            ReqParams->Uk6[2]=0x00;
+            ReqParams->Uk6[3]=0x00;
+            ReqParams->Uk6[4]=0x00;
+            ReqParams->Uk6[5]=0x00;
+            ReqParams->Dwnld_ID=0x00;
+            ReqParams->Len_1 =0x09;
+            ReqParams->Prefix=0x5F;
+            ReqParams->BlkPrfx=0x30;
+            ReqParams->BlkType=BlockType;
+			StoreBlockNum=BlockNum;
+            ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30;
+            BlockNum=BlockNum % 10000;
+            ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+            BlockNum=BlockNum % 1000;
+            ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30;
+            BlockNum=BlockNum % 100;
+            ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30;
+            BlockNum=BlockNum % 10;
+            ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30;
+            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);
+            Result=isoExchangeBuffer(0,IsoSize);
+
+            // Get Result
+            if (Result==0)
+            {
+                 if (SwapWord(Answer->Error)!=Code7NeedPassword)
+                 {
+                     if ((Answer->Error!=0) || (*ResParams!=pduReqDownload))
+                          Result=errCliDownloadSequenceFailed;
+                 }
+                 else
+                     Result=errCliNeedPassword;
+            }
+        //----------------------------------------------> Start Download request
+        if (Result==0)
+        {
+          do
+          {
+            //<-------------------------------- Download sequence (PLC requests)
+                PReqDownloadParams ReqParams;
+                PS7ResHeader23 Answer;
+                PResDownloadParams ResParams;
+                PResDownloadDataHeader ResData;
+                int Slice, Size, MaxSlice;
+                word Sequence;
+                pbyte Source;
+                pbyte Target;
+
+                ReqParams=PReqDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+                Answer   =PS7ResHeader23(&PDU.Payload);
+                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 (Result==0)
+                {
+                      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;
+
+                            // Init Params
+                            ResParams->FunDwnld=pduDownload;
+                            if (Remainder>0)
+                                ResParams->EoS=0x01;
+                            else
+                                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);
+                      }
+                      else
+                          Result=errCliDownloadSequenceFailed;
+                };
+            //--------------------------------> Download sequence (PLC requests)
+          }
+          while (!Done && (Result==0));
+
+          if (Result==0)
+          {
+            //<-------------------------------------------Perform Download Ended
+                PReqDownloadParams ReqParams;
+                PS7ResHeader23 Answer;
+                PResEndDownloadParams ResParams;
+                int Size;
+                word Sequence;
+
+                ReqParams=PReqDownloadParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+                Answer   =PS7ResHeader23(&PDU.Payload);
+                ResParams=PResEndDownloadParams(pbyte(Answer)+ResHeaderSize23);
+
+                Result=isoRecvBuffer(0,Size);
+                if (Result==0)
+                {
+                  if ((u_int(Size)>sizeof(TS7ReqHeader)) && (ReqParams->Fun==pduDownloadEnded))
+                  {
+                    Sequence=PDUH_out->Sequence;
+                    // Init Answer
+                    Answer->P=0x32;
+                    Answer->PDUType=PduType_response;
+                    Answer->AB_EX=0x0000;
+                    Answer->Sequence=Sequence;
+                    Answer->ParLen =SwapWord(sizeof(TResEndDownloadParams));
+                    Answer->DataLen=0x0000;
+                    Answer->Error  =0x0000;
+
+                    // Init Params
+                    *ResParams=pduDownloadEnded;
+                    IsoSize=ResHeaderSize23+sizeof(TResEndDownloadParams);
+                    Result=isoSendBuffer(0,IsoSize);
+                  }
+                  else
+                      Result=errCliDownloadSequenceFailed;
+                };
+            //------------------------------------------->Perform Download Ended
+            if (Result==0)
+            {
+               //<----------------------------------- Insert block into the unit
+                PReqControlBlockParams ReqParams;
+                PS7ResHeader23 Answer;
+                pbyte ResParams;
+
+                // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+                ReqParams=PReqControlBlockParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+                Answer=PS7ResHeader23(&PDU.Payload);
+                ResParams=pbyte(Answer)+ResHeaderSize23;
+                // Init Header
+                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(sizeof(TReqControlBlockParams));
+                PDUH_out->DataLen=0x0000;             // No data
+                // Init Params
+                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->BlkType=BlockType;
+                ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30;
+                BlockNum=BlockNum % 10000;
+                ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+                BlockNum=BlockNum % 1000;
+                ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30;
+                BlockNum=BlockNum % 100;
+                ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30;
+                BlockNum=BlockNum % 10;
+                ReqParams->AsciiBlk[4]=(BlockNum / 1)+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);
+                Result=isoExchangeBuffer(0,IsoSize);
+
+                if (Result==0)
+                {
+                  if ((Answer->Error!=0) || (*ResParams!=pduControl))
+                     Result=errCliInsertRefused;
+                };
+               //-----------------------------------> Insert block into the unit
+            }
+          };
+        };
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opDelete()
+{
+    PReqControlBlockParams ReqParams;
+    PS7ResHeader23 Answer;
+    pbyte ResParams;
+    int IsoSize, BlockType, BlockNum, Result;
+
+    BlockType=Job.Area;
+    BlockNum =Job.Number;
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqControlBlockParams(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=pbyte(Answer)+ResHeaderSize23;
+    // Init Header
+    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(sizeof(TReqControlBlockParams));
+    PDUH_out->DataLen=0x0000;             // No data
+    // Init Params
+    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->BlkType=BlockType;
+    ReqParams->AsciiBlk[0]=(BlockNum / 10000)+0x30;
+    BlockNum=BlockNum % 10000;
+    ReqParams->AsciiBlk[1]=(BlockNum / 1000)+0x30;
+    BlockNum=BlockNum % 1000;
+    ReqParams->AsciiBlk[2]=(BlockNum / 100)+0x30;
+    BlockNum=BlockNum % 100;
+    ReqParams->AsciiBlk[3]=(BlockNum / 10)+0x30;
+    BlockNum=BlockNum % 10;
+    ReqParams->AsciiBlk[4]=(BlockNum / 1)+0x30;
+    ReqParams->SFun =SFun_Delete;
+    ReqParams->Len_2=0x05;
+    ReqParams->Cmd[0]='_';
+    ReqParams->Cmd[1]='D';
+    ReqParams->Cmd[2]='E';
+    ReqParams->Cmd[3]='L';
+    ReqParams->Cmd[4]='E';
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqControlBlockParams);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    if (Result==0)
+    {
+        if (SwapWord(Answer->Error)!=Code7NeedPassword)
+        {
+            if ((Answer->Error!=0) || (*ResParams!=pduControl))
+              Result=errCliDeleteRefused;
+        }
+        else
+            Result=errCliNeedPassword;
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+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;
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    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;
+    Done =false;
+    do
+    {
+    //<------------------------------------------------------- read slices
+        if (First)
+        {
+         //<-------------------------------------------------- prepare first
+            DataSize=sizeof(TS7ReqSZLData);
+            // Fill Header
+            PDUH_out->P=0x32;                    // Always 0x32
+            PDUH_out->PDUType=PduType_userdata;  // 0x07
+            PDUH_out->AB_EX=0x0000;              // Always 0x0000
+            PDUH_out->Sequence=GetNextWord();    // AutoInc
+            PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLFirst)); // 8 bytes params
+            PDUH_out->DataLen=SwapWord(DataSize); // 8/4 bytes data
+            // Fill Params
+            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
+        }
+        else
+        {
+         //<-------------------------------------------------- prepare next
+            DataSize=sizeof(TS7ReqSZLData)-4;
+            // Fill Header
+            PDUH_out->P=0x32;                    // Always 0x32
+            PDUH_out->PDUType=PduType_userdata;  // 0x07
+            PDUH_out->AB_EX=0x0000;              // Always 0x0000
+            PDUH_out->Sequence=GetNextWord();    // AutoInc
+            PDUH_out->ParLen=SwapWord(sizeof(TReqFunReadSZLNext)); // 8 bytes params
+            PDUH_out->DataLen=SwapWord(DataSize);// 8/4 bytes data
+            // Fill Params
+            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;
+            // Fill Data
+            ReqDataNext->Ret      =0x0A;
+            ReqDataNext->TS       =0x00;
+            ReqDataNext->DLen     =0x0000;
+            ReqDataNext->ID       =0x0000;
+            ReqDataNext->Index    =0x0000;
+            IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunReadSZLNext)+DataSize;
+         //--------------------------------------------------> prepare next
+        }
+        Result=isoExchangeBuffer(0,IsoSize);
+        // Get Data
+        if (Result==0)
+        {
+            if (First)
+            {
+            //<------------------------------------------ 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;
+                    }
+                    else
+                        Result=CpuError(ResDataFirst->Ret);
+                }
+                else
+                    Result=CpuError(ResDataFirst->Ret);
+            //------------------------------------------> get data first
+            }
+            else
+            {
+            //<------------------------------------------ get data next
+                if (ResParams->Err==0)
+                {
+                    if (ResDataNext->Ret==0xFF)  // <-- 0xFF means Result OK
+                    {
+                        // Gets Amount of this slice
+                        DataSZL=SwapWord(ResDataNext->DLen);
+                        // 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, PDataNext, DataSZL);
+                        Offset+=DataSZL;
+                    }
+                    else
+                        Result=CpuError(ResDataNext->Ret);
+                }
+                else
+                    Result=CpuError(ResDataNext->Ret);
+            //------------------------------------------> get data next
+            }
+            First=false;
+        }
+    //-------------------------------------------------------> read slices
+    }
+    while ((!Done) && (Result==0));
+
+    // Check errors and adjust header
+    if (Result==0)
+    {
+        // Adjust big endian header
+        Header->LENTHDR=SwapWord(Header->LENTHDR);
+        Header->N_DR   =SwapWord(Header->N_DR);
+        opSize=int(Offset);
+
+        if (Job.IParam==1)  // if 1 data has to be copied into user buffer
+        {
+              // Check buffer size
+              if (opSize>Job.Amount)
+              {
+                 opSize=Job.Amount;
+                 NoRoom=true;
+              }
+              memcpy(Job.pData, &opData, opSize);
+              *Job.pAmount=opSize;
+        };
+    };
+    if ((Result==0)&& NoRoom)
+        Result=errCliBufferTooSmall;
+
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opReadSZLList()
+{
+    PS7SZLList usrSZLList, opDataList;
+    int ItemsCount, ItemsCount_in, c, Result;
+    bool NoRoom = false;
+
+    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();
+    if (Result==0)
+    {
+        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
+            NoRoom=true;
+        }
+        for (c = 0; c < ItemsCount; c++)
+          usrSZLList->List[c]=SwapWord(opDataList->List[c]);
+        *Job.pAmount=ItemsCount;
+    }
+    else
+        *Job.pAmount=0;
+
+    if ((Result==0) && NoRoom)
+        Result=errCliBufferTooSmall;
+
+    return Result;
+}
+//---------------------------------------------------------------------------
+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;
+    PS7ResParams7 ResParams;
+    PResDataGetTime ResData;
+    PS7ResHeader17 Answer;
+    int IsoSize, Result;
+    word AYear;
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunDateTime(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ReqData  =PReqDataGetDateTime(pbyte(ReqParams)+sizeof(TReqFunDateTime));
+    Answer   =PS7ResHeader17(&PDU.Payload);
+    ResParams=PS7ResParams7(pbyte(Answer)+ResHeaderSize17);
+    ResData  =PResDataGetTime(pbyte(ResParams)+sizeof(TS7Params7));
+    DateTime =PTimeStruct(Job.pData);
+    // Fill Header
+    PDUH_out->P=0x32;                    // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;  // 0x07
+    PDUH_out->AB_EX=0x0000;              // Always 0x0000
+    PDUH_out->Sequence=GetNextWord();    // AutoInc
+    PDUH_out->ParLen=SwapWord(sizeof(TReqFunDateTime));   // 8 bytes params
+    PDUH_out->DataLen=SwapWord(sizeof(TReqDataGetDateTime)); // 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     =grClock;
+    ReqParams->SubFun =SFun_ReadClock;
+    ReqParams->Seq    =0x00;
+    // Fill Data
+    *ReqData =0x0000000A;
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunDateTime)+sizeof(TReqDataGetDateTime);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    // Get Data
+    if (Result==0)
+    {
+        if (ResParams->Err==0)
+        {
+            if (ResData->RetVal==0xFF) // <-- 0xFF means Result OK
+            {
+                // 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;
+            }
+            else
+                Result=CpuError(ResData->RetVal);
+        }
+        else
+            Result=CpuError(ResData->RetVal);
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opSetDateTime()
+{
+    PTimeStruct DateTime;
+    PReqFunDateTime ReqParams;
+    PReqDataSetTime ReqData;
+    PS7ResParams7 ResParams;
+    PS7ResHeader17 Answer;
+    word AYear;
+    int IsoSize, Result;
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunDateTime(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ReqData  =PReqDataSetTime(pbyte(ReqParams)+sizeof(TReqFunDateTime));
+    Answer   =PS7ResHeader17(&PDU.Payload);
+    ResParams=PS7ResParams7(pbyte(Answer)+ResHeaderSize17);
+    DateTime =PTimeStruct(Job.pData);
+    // Fill Header
+    PDUH_out->P=0x32;                    // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;  // 0x07
+    PDUH_out->AB_EX=0x0000;              // Always 0x0000
+    PDUH_out->Sequence=GetNextWord();    // AutoInc
+    PDUH_out->ParLen=SwapWord(sizeof(TReqFunDateTime));  // 8 bytes params
+    PDUH_out->DataLen=SwapWord(sizeof(TReqDataSetTime)); // 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     =grClock;
+    ReqParams->SubFun =SFun_SetClock;
+    ReqParams->Seq    =0x00;
+    // EncodeSiemensDateTime;
+    if (DateTime->tm_year<100)
+        AYear=DateTime->tm_year;
+    else
+        AYear=DateTime->tm_year-100;
+
+    ReqData->RetVal=0xFF;
+    ReqData->TSize =TS_ResOctet;
+    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);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    // Get Result
+    if (Result==0)
+    {
+       if (ResParams->Err!=0)
+          Result=CpuError(SwapWord(ResParams->Err));
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opGetOrderCode()
+{
+    PS7OrderCode OC;
+    int Result;
+
+    Job.ID     =0x0011;
+    Job.Index  =0x0000;
+    Job.IParam =0;
+    Result     =opReadSZL();
+    if (Result==0)
+    {
+        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];
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opGetCpuInfo()
+{
+    PS7CpuInfo Info;
+    int Result;
+
+    // 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.Index =0x0000;
+    Job.IParam=0;
+    Result    =opReadSZL();
+    if (Result==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);
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opGetCpInfo()
+{
+    PS7CpInfo Info;
+    int Result;
+    // Store Pointer
+    Info=PS7CpInfo(Job.pData);
+    memset(Info,0,sizeof(TS7CpInfo));
+    Job.ID    =0x0131;
+    Job.Index =0x0001;
+    Job.IParam=0;
+    Result    =opReadSZL();
+    if (Result==0)
+    {
+        Info->MaxPduLengt=opData[6]*256+opData[7];
+        Info->MaxConnections=opData[8]*256+opData[9];
+        Info->MaxMpiRate=DWordAt(&opData[10]);
+        Info->MaxBusRate=DWordAt(&opData[14]);
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opGetPlcStatus()
+{
+    int *Status;
+    int Result;
+
+    Status =(int*)Job.pData;
+    Job.ID     =0x0424;
+    Job.Index  =0x0000;
+    Job.IParam =0;
+    Result     =opReadSZL();
+    if (Result==0)
+    {
+        switch (opData[7])
+        {
+            case S7CpuStatusUnknown :
+            case S7CpuStatusRun     :
+            case S7CpuStatusStop    : *Status=opData[7];
+            break;
+            default :
+            // 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;
+        }
+    }
+    else
+        *Status=0;
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opPlcStop()
+{
+    PReqFunPlcStop ReqParams;
+    PResFunCtrl ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, Result;
+
+    char p_program[] = {'P','_','P','R','O','G','R','A','M'};
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunPlcStop(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+    // Fill Header
+    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(sizeof(TReqFunPlcStop));
+    PDUH_out->DataLen=0x0000;            // No Data
+    // Fill Params
+    ReqParams->Fun=pduStop;
+    memset(ReqParams->Uk_5,0,5);
+    ReqParams->Len_2=0x09;
+    memcpy(ReqParams->Cmd,&p_program,9);
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcStop);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    if (Result==0)
+    {
+        if (Answer->Error!=0)
+        {
+          if (ResParams->ResFun!=pduStop)
+             Result=errCliCannotStopPLC;
+          else
+             if (ResParams->para ==0x07)
+                 Result=errCliAlreadyStop;
+             else
+                 Result=errCliCannotStopPLC;
+        };
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opPlcHotStart()
+{
+    PReqFunPlcHotStart ReqParams;
+    PResFunCtrl ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, Result;
+
+    char p_program[] = {'P','_','P','R','O','G','R','A','M'};
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunPlcHotStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+    // Fill Header
+    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(sizeof(TReqFunPlcHotStart));   // 16 bytes params
+    PDUH_out->DataLen=0x0000;            // No Data
+    // Fill 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;
+    ReqParams->Len_2=0x09;
+    memcpy(ReqParams->Cmd,&p_program,9);
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcHotStart);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    if (Result==0)
+    {
+        if ((Answer->Error!=0))
+        {
+          if ((ResParams->ResFun!=pduStart))
+              Result=errCliCannotStartPLC;
+          else
+          {
+              if (ResParams->para==0x03)
+                  Result=errCliAlreadyRun;
+              else
+                  if (ResParams->para==0x02)
+                      Result=errCliCannotStartPLC;
+                  else
+                      Result=errCliCannotStartPLC;
+          }
+        }
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opPlcColdStart()
+{
+    PReqFunPlcColdStart ReqParams;
+    PResFunCtrl ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, Result;
+    char p_program[] = {'P','_','P','R','O','G','R','A','M'};
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunPlcColdStart(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+    // Fill Header
+    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(sizeof(TReqFunPlcColdStart));   // 22 bytes params
+    PDUH_out->DataLen=0x0000;             // No Data
+    // Fill 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=SwapWord(0x0002);
+    ReqParams->SFun =SwapWord(0x4320); // Cold start
+    ReqParams->Len_2=0x09;
+    memcpy(ReqParams->Cmd,&p_program,9);
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunPlcColdStart);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    if (Result==0)
+    {
+        if ((Answer->Error!=0))
+        {
+          if ((ResParams->ResFun!=pduStart))
+              Result=errCliCannotStartPLC;
+          else
+          {
+              if (ResParams->para==0x03)
+                  Result=errCliAlreadyRun;
+              else
+                  if (ResParams->para==0x02)
+                      Result=errCliCannotStartPLC;
+                  else
+                      Result=errCliCannotStartPLC;
+          }
+        }
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opCopyRamToRom()
+{
+    PReqFunCopyRamToRom ReqParams;
+    PResFunCtrl ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, CurTimeout, Result;
+    char _modu[] = {'_','M','O','D','U'};
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunCopyRamToRom(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+    // Fill Header
+    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(sizeof(TReqFunCopyRamToRom));
+    PDUH_out->DataLen=0x0000;             // No Data
+    // Fill Params
+    ReqParams->Fun=pduControl;
+    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=SwapWord(0x0002);
+    ReqParams->SFun =SwapWord(0x4550);
+    ReqParams->Len_2=0x05;
+    memcpy(ReqParams->Cmd,&_modu,5);
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCopyRamToRom);
+    // Changes the timeout
+    CurTimeout=RecvTimeout;
+    RecvTimeout=Job.IParam;
+    Result=isoExchangeBuffer(0,IsoSize);
+    // Restores the timeout
+    RecvTimeout=CurTimeout;
+
+    if (Result==0)
+    {
+        if ((Answer->Error!=0) || (ResParams->ResFun!=pduControl))
+             Result=errCliCannotCopyRamToRom;
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opCompress()
+{
+    PReqFunCompress ReqParams;
+    PResFunCtrl ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, CurTimeout, Result;
+    char _garb[] = {'_','G','A','R','B'};
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunCompress(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResFunCtrl(pbyte(Answer)+ResHeaderSize23);
+    // Fill Header
+    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(sizeof(TReqFunCompress));
+    PDUH_out->DataLen=0x0000;            // No Data
+    // Fill Params
+    ReqParams->Fun=pduControl;
+    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;
+    ReqParams->Len_2=0x05;
+    memcpy(ReqParams->Cmd,&_garb,5);
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunCompress);
+    // Changes the timeout
+    CurTimeout=RecvTimeout;
+    RecvTimeout=Job.IParam;
+    Result=isoExchangeBuffer(0,IsoSize);
+    // Restores the timeout
+    RecvTimeout=CurTimeout;
+
+    if (Result==0)
+    {
+         if (((Answer->Error!=0) || (ResParams->ResFun!=pduControl)))
+              Result=errCliCannotCompress;
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opGetProtection()
+{
+    PS7Protection Info, usrInfo;
+    int Result;
+
+    // Store Pointer
+    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
+    Result    =opReadSZL();
+    if (Result==0)
+    {
+        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);
+    }
+    return Result;
+}
+//******************************************************************************
+//                                     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;
+    PS7ResHeader23 Answer;
+    int c, IsoSize, Result;
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunSecurity(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ReqData  =PReqDataSecurity(pbyte(ReqParams)+sizeof(TReqFunSecurity));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResParamsSecurity(pbyte(Answer)+ResHeaderSize17);
+    // Fill Header
+    PDUH_out->P=0x32;                    // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;  // 0x07
+    PDUH_out->AB_EX=0x0000;              // Always 0x0000
+    PDUH_out->Sequence=GetNextWord();    // AutoInc
+    PDUH_out->ParLen =SwapWord(sizeof(TReqFunSecurity));
+    PDUH_out->DataLen=SwapWord(sizeof(TReqDataSecurity));
+    // Fill params (mostly constants)
+    ReqParams->Head[0]=0x00;
+    ReqParams->Head[1]=0x01;
+    ReqParams->Head[2]=0x12;
+    ReqParams->Plen   =0x04;
+    ReqParams->Uk     =0x11;
+    ReqParams->Tg     =grSecurity;
+    ReqParams->SubFun =SFun_EnterPwd;
+    ReqParams->Seq    =0x00;
+    // Fill Data
+    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);
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    // Get Return
+    if (Result==0)
+    {
+        if (ResParams->Err!=0)
+              Result=CpuError(SwapWord(ResParams->Err));
+    };
+
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::opClearPassword()
+{
+    PReqFunSecurity ReqParams;
+    PReqDataSecurity ReqData;
+    PResParamsSecurity ResParams;
+    PS7ResHeader23 Answer;
+    int IsoSize, Result;
+
+    // Setup pointers (note : PDUH_out and PDU.Payload are the same pointer)
+    ReqParams=PReqFunSecurity(pbyte(PDUH_out)+sizeof(TS7ReqHeader));
+    ReqData  =PReqDataSecurity(pbyte(ReqParams)+sizeof(TReqFunSecurity));
+    Answer   =PS7ResHeader23(&PDU.Payload);
+    ResParams=PResParamsSecurity(pbyte(Answer)+ResHeaderSize17);
+    // Fill Header
+    PDUH_out->P=0x32;                     // Always 0x32
+    PDUH_out->PDUType=PduType_userdata;   // 0x07
+    PDUH_out->AB_EX=0x0000;               // Always 0x0000
+    PDUH_out->Sequence=GetNextWord();     // AutoInc
+    PDUH_out->ParLen =SwapWord(sizeof(TReqFunSecurity));
+    PDUH_out->DataLen=SwapWord(0x0004);   // We need only 4 bytes
+    // Fill params (mostly constants)
+    ReqParams->Head[0]=0x00;
+    ReqParams->Head[1]=0x01;
+    ReqParams->Head[2]=0x12;
+    ReqParams->Plen   =0x04;
+    ReqParams->Uk     =0x11;
+    ReqParams->Tg     =grSecurity;
+    ReqParams->SubFun =SFun_CancelPwd;
+    ReqParams->Seq    =0x00;
+    // Fill Data
+    ReqData->Ret      =0x0A;
+    ReqData->TS       =0x00;
+    ReqData->DLen     =0x0000;
+
+    IsoSize=sizeof(TS7ReqHeader)+sizeof(TReqFunSecurity)+4;
+    Result=isoExchangeBuffer(0,IsoSize);
+
+    // Get Return
+    if (Result==0)
+    {
+       if (ResParams->Err!=0)
+            Result=CpuError(SwapWord(ResParams->Err));
+    };
+    return Result;
+}
+//---------------------------------------------------------------------------
+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;
+  default:
+    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))
+    return errCliInvalidBlockSize;
+
+  return 0;
+}
+//---------------------------------------------------------------------------
+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;
+    default : return 0;
+  };
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::PerformOperation()
+{
+    ClrError();
+    int Operation=Job.Op;
+    switch(Operation)
+    {
+        case s7opNone:
+             Job.Result=errCliInvalidParams;
+             break;
+        case s7opReadArea:
+             Job.Result=opReadArea();
+             break;
+        case s7opWriteArea:
+             Job.Result=opWriteArea();
+             break;
+        case s7opReadMultiVars:
+             Job.Result=opReadMultiVars();
+             break;
+        case s7opWriteMultiVars:
+             Job.Result=opWriteMultiVars();
+             break;
+        case s7opDBGet:
+             Job.Result=opDBGet();
+             break;
+        case s7opDBFill:
+             Job.Result=opDBFill();
+             break;
+        case s7opUpload:
+             Job.Result=opUpload();
+             break;
+        case s7opDownload:
+             Job.Result=opDownload();
+             break;
+        case s7opDelete:
+             Job.Result=opDelete();
+             break;
+        case s7opListBlocks:
+             Job.Result=opListBlocks();
+             break;
+        case s7opAgBlockInfo:
+             Job.Result=opAgBlockInfo();
+             break;
+        case s7opListBlocksOfType:
+             Job.Result=opListBlocksOfType();
+             break;
+        case s7opReadSzlList:
+             Job.Result=opReadSZLList();
+             break;
+        case s7opReadSZL:
+             Job.Result=opReadSZL();
+             break;
+        case s7opGetDateTime:
+             Job.Result=opGetDateTime();
+             break;
+        case s7opSetDateTime:
+             Job.Result=opSetDateTime();
+             break;
+        case s7opGetOrderCode:
+             Job.Result=opGetOrderCode();
+             break;
+        case s7opGetCpuInfo:
+             Job.Result=opGetCpuInfo();
+             break;
+        case s7opGetCpInfo:
+             Job.Result=opGetCpInfo();
+             break;
+        case s7opGetPlcStatus:
+             Job.Result=opGetPlcStatus();
+             break;
+        case s7opPlcHotStart:
+             Job.Result=opPlcHotStart();
+             break;
+        case s7opPlcColdStart:
+             Job.Result=opPlcColdStart();
+             break;
+        case s7opCopyRamToRom:
+             Job.Result=opCopyRamToRom();
+             break;
+        case s7opCompress:
+             Job.Result=opCompress();
+             break;
+        case s7opPlcStop:
+             Job.Result=opPlcStop();
+             break;
+        case s7opGetProtection:
+             Job.Result=opGetProtection();
+             break;
+        case s7opSetPassword:
+             Job.Result=opSetPassword();
+             break;
+        case s7opClearPassword:
+             Job.Result=opClearPassword();
+             break;
+    }
+   Job.Time =SysGetTick()-JobStart;
+   Job.Pending=false;
+   return SetError(Job.Result);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Disconnect()
+{
+     JobStart=SysGetTick();
+     PeerDisconnect();
+     Job.Time=SysGetTick()-JobStart;
+	 Job.Pending=false;
+     return 0;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Reset(bool DoReconnect)
+{
+    Job.Pending=false;
+    if (DoReconnect) {
+        Disconnect();
+        return Connect();
+    }
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Connect()
+{
+	 int Result;
+	 JobStart=SysGetTick();
+	 Result  =PeerConnect();
+	 Job.Time=SysGetTick()-JobStart;
+	 return Result;
+}
+//---------------------------------------------------------------------------
+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);
+    return Connect();
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetParam(int ParamNumber, void *pValue)
+{
+    switch (ParamNumber)
+    {
+	case p_u16_RemotePort:
+		*Puint16_t(pValue)=RemotePort;
+		break;
+	case p_i32_PingTimeout:
+		*Pint32_t(pValue)=PingTimeout;
+		break;
+	case p_i32_SendTimeout:
+		*Pint32_t(pValue)=SendTimeout;
+		break;
+	case p_i32_RecvTimeout:
+		*Pint32_t(pValue)=RecvTimeout;
+		break;
+	case p_i32_WorkInterval:
+		*Pint32_t(pValue)=WorkInterval;
+		break;
+	case p_u16_SrcRef:
+		*Puint16_t(pValue)=SrcRef;
+		break;
+	case p_u16_DstRef:
+		*Puint16_t(pValue)=DstRef;
+		break;
+	case p_u16_SrcTSap:
+		*Puint16_t(pValue)=SrcTSap;
+		break;
+	case p_i32_PDURequest:
+		*Pint32_t(pValue)=PDURequest;
+		break;
+	default: return errCliInvalidParamNumber;
+    }
+    return 0;
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::SetParam(int ParamNumber, void *pValue)
+{
+    switch (ParamNumber)
+    {
+	case p_u16_RemotePort:
+		if (!Connected)
+            RemotePort=*Puint16_t(pValue);
+		else
+		    return errCliCannotChangeParam;
+		break;
+	case p_i32_PingTimeout:
+		PingTimeout=*Pint32_t(pValue);
+		break;
+	case p_i32_SendTimeout:
+		SendTimeout=*Pint32_t(pValue);
+		break;
+	case p_i32_RecvTimeout:
+		RecvTimeout=*Pint32_t(pValue);
+		break;
+	case p_i32_WorkInterval:
+		WorkInterval=*Pint32_t(pValue);
+		break;
+	case p_u16_SrcRef:
+		SrcRef=*Puint16_t(pValue);
+		break;
+	case p_u16_DstRef:
+		DstRef=*Puint16_t(pValue);
+		break;
+	case p_u16_SrcTSap:
+		SrcTSap=*Puint16_t(pValue);
+		break;
+	case p_i32_PDURequest:
+		PDURequest=*Pint32_t(pValue);
+		break;
+	default: return errCliInvalidParamNumber;
+    }
+    return 0;
+}
+//---------------------------------------------------------------------------
+// Data I/O functions
+int TSnap7MicroClient::ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen,  void * pUsrData)
+{
+     if (!Job.Pending)
+     {
+         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();
+         return PerformOperation();
+     }
+     else
+         return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen,  void * pUsrData)
+{
+     if (!Job.Pending)
+     {
+          Job.Pending  = true;
+          Job.Op       = s7opWriteArea;
+          Job.Area     = Area;
+          Job.Number   = DBNumber;
+          Job.Start    = Start;
+          Job.Amount   = Amount;
+          Job.WordLen  = WordLen;
+          Job.pData    = pUsrData;
+          JobStart     = SysGetTick();
+          return PerformOperation();
+     }
+     else
+          return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::ReadMultiVars(PS7DataItem Item, int ItemsCount)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opReadMultiVars;
+        Job.Amount   =ItemsCount;
+        Job.pData    =Item;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+    	return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::WriteMultiVars(PS7DataItem Item, int ItemsCount)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opWriteMultiVars;
+        Job.Amount   =ItemsCount;
+        Job.pData    =Item;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+    	return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+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)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opListBlocks;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+    	return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetAgBlockInfo(int BlockType, int BlockNum, PS7BlockInfo pUsrData)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opAgBlockInfo;
+        Job.Area     =BlockType;
+        Job.Number   =BlockNum;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetPgBlockInfo(void * pBlock, PS7BlockInfo pUsrData, int Size)
+{
+    PS7CompactBlockInfo Info;
+    PS7BlockFooter Footer;
+
+    int Result=CheckBlock(-1,-1,pBlock,Size);
+    if (Result==0)
+    {
+        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 (!Job.Pending)
+    {
+	if (ItemsCount<1)
+	    return SetError(errCliInvalidBlockSize);
+	Job.Pending  =true;
+	Job.Op       =s7opListBlocksOfType;
+	Job.Area     =BlockType;
+	Job.pData    =pUsrData;
+	Job.pAmount  =&ItemsCount;
+	Job.Amount   =ItemsCount;
+	JobStart     =SysGetTick();
+	return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Upload(int BlockType, int BlockNum,  void * pUsrData, int & Size)
+{
+    if (!Job.Pending)
+    {
+        if (Size<=0)
+	     return SetError(errCliInvalidBlockSize);
+        Job.Pending  =true;
+        Job.Op       =s7opUpload;
+        Job.Area     =BlockType;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.Number   =BlockNum;
+        Job.IParam   =0; // not full upload, only data
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::FullUpload(int BlockType, int BlockNum, void * pUsrData, int & Size)
+{
+    if (!Job.Pending)
+    {
+        if (Size<=0)
+            return SetError(errCliInvalidBlockSize);
+        Job.Pending  =true;
+        Job.Op       =s7opUpload;
+        Job.Area     =BlockType;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.Number   =BlockNum;
+        Job.IParam   =1; // header + data + footer
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Download(int BlockNum,  void * pUsrData,  int Size)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opDownload;
+        memcpy(&opData, pUsrData, Size);
+        Job.Number   =BlockNum;
+        Job.Amount   =Size;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Delete(int BlockType, int BlockNum)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opDelete;
+        Job.Area     =BlockType;
+        Job.Number   =BlockNum;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::DBGet(int DBNumber, void * pUsrData, int & Size)
+{
+    if (!Job.Pending)
+    {
+        if (Size<=0)
+            return SetError(errCliInvalidBlockSize);
+        Job.Pending  =true;
+        Job.Op       =s7opDBGet;
+        Job.Number   =DBNumber;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::DBFill(int DBNumber,  int FillChar)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opDBFill;
+        Job.Number   =DBNumber;
+        Job.IParam   =FillChar;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetPlcDateTime(tm &DateTime)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetDateTime;
+        Job.pData    =&DateTime;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::SetPlcDateTime(tm * DateTime)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opSetDateTime;
+        Job.pData    =DateTime;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::SetPlcSystemDateTime()
+{
+    time_t Now;
+    time(&Now);
+    struct tm * DateTime = localtime (&Now);
+    return SetPlcDateTime(DateTime);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetOrderCode(PS7OrderCode pUsrData)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetOrderCode;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetCpuInfo(PS7CpuInfo pUsrData)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetCpuInfo;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetCpInfo(PS7CpInfo pUsrData)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetCpInfo;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::ReadSZL(int ID, int Index, PS7SZL pUsrData, int &Size)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opReadSZL;
+        Job.ID       =ID;
+        Job.Index    =Index;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&Size;
+        Job.Amount   =Size;
+        Job.IParam   =1; // Data has to be copied into user buffer
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::ReadSZLList(PS7SZLList pUsrData, int &ItemsCount)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opReadSzlList;
+        Job.pData    =pUsrData;
+        Job.pAmount  =&ItemsCount;
+        Job.Amount   =ItemsCount;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::PlcHotStart()
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opPlcHotStart;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::PlcColdStart()
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opPlcColdStart;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::PlcStop()
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opPlcStop;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::CopyRamToRom(int Timeout)
+{
+      if (!Job.Pending)
+      {
+          if (Timeout>0)
+          {
+               Job.Pending =true;
+               Job.Op      =s7opCopyRamToRom;
+               Job.IParam  =Timeout;
+               JobStart    =SysGetTick();
+               return PerformOperation();
+          }
+          else
+              return SetError(errCliInvalidParams);
+      }
+      else
+          return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::Compress(int Timeout)
+{
+      if (!Job.Pending)
+      {
+          if (Timeout>0)
+          {
+               Job.Pending =true;
+               Job.Op      =s7opCompress;
+               Job.IParam  =Timeout;
+               JobStart    =SysGetTick();
+               return PerformOperation();
+          }
+          else
+              return SetError(errCliInvalidParams);
+      }
+      else
+          return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetPlcStatus(int & Status)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetPlcStatus;
+        Job.pData    =&Status;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::GetProtection(PS7Protection pUsrData)
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opGetProtection;
+        Job.pData    =pUsrData;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::SetSessionPassword(char *Password)
+{
+    if (!Job.Pending)
+    {
+        size_t L = strlen(Password);
+        // checks the len
+        if ((L<1) || (L>8))
+           return SetError(errCliInvalidParams);
+        Job.Pending  =true;
+        // prepares an 8 char string filled with spaces
+        memset(&opData,0x20,8);
+        // copies
+        strncpy((char*)&opData,Password,L);
+        Job.Op       =s7opSetPassword;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+int TSnap7MicroClient::ClearSessionPassword()
+{
+    if (!Job.Pending)
+    {
+        Job.Pending  =true;
+        Job.Op       =s7opClearPassword;
+        JobStart     =SysGetTick();
+        return PerformOperation();
+    }
+    else
+        return SetError(errCliJobPending);
+}
+//---------------------------------------------------------------------------
+

+ 364 - 0
src/core/s7_micro_client.h

@@ -0,0 +1,364 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_micro_client_h
+#define s7_micro_client_h
+//---------------------------------------------------------------------------
+#include "s7_peer.h"
+//---------------------------------------------------------------------------
+
+const longword errCliMask                   = 0xFFF00000;
+const longword errCliBase                   = 0x000FFFFF;
+
+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 time_t DeltaSecs = 441763200; // Seconds between 1970/1/1 (C time base) and 1984/1/1 (Siemens base)
+
+#pragma pack(1)
+
+// Read/Write Multivars
+typedef struct{
+   int   Area;
+   int   WordLen;
+   int   Result;
+   int   DBNumber;
+   int   Start;
+   int   Amount;
+   void  *pdata;
+} TS7DataItem, *PS7DataItem;
+
+typedef int TS7ResultItems[MaxVars];
+typedef TS7ResultItems *PS7ResultItems;
+
+typedef struct {
+   int OBCount;
+   int FBCount;
+   int FCCount;
+   int SFBCount;
+   int SFCCount;
+   int DBCount;
+   int SDBCount;
+} TS7BlocksList, *PS7BlocksList;
+
+typedef struct {
+   int BlkType;
+   int BlkNumber;
+   int BlkLang;
+   int BlkFlags;
+   int MC7Size;  // The real size in bytes
+   int LoadSize;
+   int LocalData;
+   int SBBLength;
+   int CheckSum;
+   int Version;
+   // Chars info
+   char CodeDate[11];
+   char IntfDate[11];
+   char Author[9];
+   char Family[9];
+   char Header[9];
+} TS7BlockInfo, *PS7BlockInfo ;
+
+typedef word TS7BlocksOfType[0x2000];
+typedef TS7BlocksOfType *PS7BlocksOfType;
+
+typedef struct {
+   char Code[21]; // Order Code
+   byte V1;       // Version V1.V2.V3
+   byte V2;
+   byte V3;
+} TS7OrderCode, *PS7OrderCode;
+
+typedef struct {
+   char ModuleTypeName[33];
+   char SerialNumber[25];
+   char ASName[25];
+   char Copyright[27];
+   char ModuleName[25];
+} TS7CpuInfo, *PS7CpuInfo;
+
+typedef struct {
+   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
+typedef struct {
+   word LENTHDR;
+   word N_DR;
+} SZL_HEADER, *PSZL_HEADER;
+
+typedef struct {
+   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
+typedef struct {
+   SZL_HEADER Header;
+   word List[0x2000-2];
+} TS7SZLList, *PS7SZLList;
+
+// See §33.19 of "System Software for S7-300/400 System and Standard Functions"
+typedef struct {
+   word  sch_schal;
+   word  sch_par;
+   word  sch_rel;
+   word  bart_sch;
+   word  anl_sch;
+} TS7Protection, *PS7Protection;
+
+#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;
+
+// Client Connection Type
+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 
+
+#pragma pack()
+
+// 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
+{
+private:
+    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);
+protected:
+    word ConnectionType;
+    longword JobStart;
+    TSnap7Job Job;
+    int DataSizeByte(int WordLength);
+    int opSize; // last operation size
+    int PerformOperation();
+public:
+    TS7Buffer opData;
+	TSnap7MicroClient();
+    ~TSnap7MicroClient();
+    int Reset(bool DoReconnect);
+    void SetConnectionParams(const char *RemAddress, word LocalTSAP, word RemoteTsap);
+    void SetConnectionType(word ConnType);
+	int ConnectTo(const char *RemAddress, int Rack, int Slot);
+    int Connect();
+	int Disconnect();
+	int GetParam(int ParamNumber, void *pValue);
+	int SetParam(int ParamNumber, void *pValue);
+    // 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);
+    int ReadMultiVars(PS7DataItem Item, int ItemsCount);
+    int WriteMultiVars(PS7DataItem Item, int ItemsCount);
+    // 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);
+    // 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);
+    // Control functions
+    int PlcHotStart();
+    int PlcColdStart();
+    int PlcStop();
+    int CopyRamToRom(int Timeout);
+    int Compress(int Timeout);
+    int GetPlcStatus(int &Status);
+    // Security functions
+    int GetProtection(PS7Protection pUsrData);
+    int SetSessionPassword(char *Password);
+    int ClearSessionPassword();
+    // Properties
+    bool Busy(){ return Job.Pending; };
+    int Time(){ return int(Job.Time);}
+};
+
+typedef TSnap7MicroClient *PSnap7MicroClient;
+
+//---------------------------------------------------------------------------
+#endif // s7_micro_client_h

+ 1178 - 0
src/core/s7_partner.cpp

@@ -0,0 +1,1178 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "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()
+{
+	int c;
+    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)
+{
+    int c;
+    Lock();
+    for (c = 0; c < MaxAdapters; c++)
+    {
+        if (Servers[c]==0)
+        {
+	    Servers[c]=Server;
+            ServersCount++;
+            break;
+		}
+    }
+    Unlock();
+}
+//---------------------------------------------------------------------------
+int TServersManager::CreateServer(longword BindAddress, PConnectionServer &Server)
+{
+    in_addr sin;
+    sin.s_addr=BindAddress;
+    int Result;
+
+    if (ServersCount<MaxAdapters)
+    {
+        Server = new TConnectionServer();
+        Result=Server->StartTo(inet_ntoa(sin));
+        if (Result!=0)
+        {
+            delete Server;
+            Server=0;
+            return Result;
+        }
+        AddServer(Server);
+        return 0;
+    }
+    else
+        return errServerNoRoom;
+}
+//---------------------------------------------------------------------------
+int TServersManager::GetServer(longword BindAddress, PConnectionServer &Server)
+{
+    int c;
+    Server=0;
+    for (c = 0; c < ServersCount; c++)
+    {
+        if (Servers[c]->LocalBind==BindAddress)
+        {
+            Server=Servers[c];
+            break;
+        }
+	}
+    if (Server==0)
+        return CreateServer(BindAddress, Server);
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+void TServersManager::RemovePartner(PConnectionServer Server, PSnap7Partner Partner)
+{
+    int c;
+    Server->RemovePartner(Partner);
+    if (Server->PartnersCount==0)
+    {
+        Lock();
+        for (c = 0; c < MaxAdapters; c++)
+        {
+            if (Servers[c]==Server)
+            {
+                Servers[c]=0;
+                ServersCount--;
+                break;
+            }
+        }
+        Unlock();
+        delete Server;
+    }
+}
+//---------------------------------------------------------------------------
+// CONNECTION SERVER
+//------------------------------------------------------------------------------
+void TConnListenerThread::Execute()
+{
+    socket_t Sock;
+    bool Valid;
+
+    while (!Terminated)
+    {
+	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);
+	    }
+	    else
+		if (Valid)
+		    Msg_CloseSocket(Sock);
+	};
+    }
+}
+//------------------------------------------------------------------------------
+TConnectionServer::TConnectionServer()
+{
+    cs = new TSnapCriticalSection;
+    memset(Partners,0,sizeof(Partners));
+    FRunning = false;
+    PartnersCount = 0;
+}
+//------------------------------------------------------------------------------
+TConnectionServer::~TConnectionServer()
+{
+    Stop();
+    delete cs;
+}
+//---------------------------------------------------------------------------
+void TConnectionServer::Lock()
+{
+    cs->Enter();
+}
+void TConnectionServer::Unlock()
+{
+    cs->Leave();
+}
+//---------------------------------------------------------------------------
+int TConnectionServer::Start()
+{
+    int Result;
+    // Creates the listener
+    SockListener = new TMsgSocket();
+    strncpy(SockListener->LocalAddress,FLocalAddress,16);
+    SockListener->LocalPort=isoTcpPort;
+    // Binds
+    Result=SockListener->SckBind();
+    if (Result==0)
+    {
+        LocalBind=SockListener->LocalBind;
+        // Listen
+        Result=SockListener->SckListen();
+        if (Result==0)
+        {
+            // Creates the Listener thread
+            ServerThread = new TConnListenerThread(SockListener, this);
+            ServerThread->Start();
+        }
+        else
+            delete SockListener;
+    }
+    else
+        delete SockListener;
+
+    FRunning=Result==0;
+    return Result;
+}
+//---------------------------------------------------------------------------
+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
+        delete SockListener;
+        FRunning = false;
+    }
+}
+//---------------------------------------------------------------------------
+PSnap7Partner TConnectionServer::FindPartner(longword Address)
+{
+    int c;
+    PSnap7Partner Result;
+    for (c = 0; c < MaxPartners; c++)
+    {
+        Result=Partners[c];
+        if (Result!=NULL)
+        {
+           if (Result->PeerAddress==Address)
+               return Result;
+        }
+    }
+    return NULL;
+}
+//------------------------------------------------------------------------------
+int TConnectionServer::FirstFree()
+{
+    int i;
+    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)
+    {
+       Lock();
+       idx=FirstFree();
+       if (idx>=0)
+       {
+           Partners[idx]=Partner;
+           PartnersCount++;
+       }
+       Unlock();
+       if (idx>=0)
+           return 0;
+       else
+           return errParNoRoom;
+    }
+    else
+       return errParAddressInUse;
+}
+//------------------------------------------------------------------------------
+void TConnectionServer::RemovePartner(PSnap7Partner Partner)
+{
+    int c;
+    Lock();
+	for (c = 0; c < MaxPartners; c++)
+    {
+        if (Partners[c]==Partner)
+        {
+            Partners[c]=0;
+            PartnersCount--;
+            break;
+        }
+    }
+    Unlock();
+}
+//------------------------------------------------------------------------------
+void TConnectionServer::Incoming(socket_t Sock)
+{
+    longword Address;
+    PSnap7Partner Partner;
+
+    Address=Msg_GetSockAddr(Sock);
+    // Looks for a partner that is waiting for a connection from this address
+    Lock();
+    Partner=FindPartner(Address);
+    Unlock();
+    // 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);
+    else
+        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()))
+			SysSleep(FRecoveryTime);
+        // 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()
+{
+    Stop();
+    OnBRecv = 0;
+    OnBSend = 0;
+    delete SendEvt;
+    delete RecvEvt;
+}
+//------------------------------------------------------------------------------
+byte TSnap7Partner::GetNextByte()
+{
+    NextByte++;
+    if (NextByte==0xFF)
+       NextByte=1;
+    return NextByte;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::Start()
+{
+    int Result;
+    PeerAddress=inet_addr(RemoteAddress);
+    SrcAddress =inet_addr(LocalAddress);
+    if (!Running)
+    {
+      if (!Active)
+      {
+          Result=ServersManager_GetServer(SrcAddress,FServer);
+          if (Result==0)
+              FServer->RegisterPartner(this);
+          BindError=Result!=0;
+      }
+      else
+      {
+          Linked=PeerConnect()==0;
+          Result=0; // we need to create the worker thread even tough it's not linked
+      };
+     // if ok create the worker thread
+     if (Result==0)
+     {
+         FWorkerThread = new TPartnerThread(this, RecoveryTime);
+         FWorkerThread->Start();
+     }
+    }
+    else
+        Result=0;
+
+    Running=Result==0;
+
+    return Result;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap)
+{
+    SrcTSap=LocTsap;
+    DstTSap=RemTsap;
+	strcpy(LocalAddress,LocAddress);
+	strcpy(RemoteAddress,RemAddress);
+    return Start();
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::Stop()
+{
+    if (Running)
+    {
+        Stopping=true; // to prevent incoming connections
+        CloseWorker();
+        if (!Active && (FServer!=0))
+            ServersManager_RemovePartner(FServer, this);
+        if (Connected)
+            Disconnect();
+        Running =false;
+        Stopping=false;
+    };
+    BindError=false;
+    return 0;
+}
+//------------------------------------------------------------------------------
+void TSnap7Partner::Disconnect()
+{
+    PeerDisconnect();
+    Linked=false;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::GetParam(int ParamNumber, void * pValue)
+{
+	switch (ParamNumber)
+	{
+		case p_u16_LocalPort:
+			*Puint16_t(pValue)=LocalPort;
+			break;
+		case p_u16_RemotePort:
+			*Puint16_t(pValue)=RemotePort;
+			break;
+		case p_i32_PingTimeout:
+			*Pint32_t(pValue)=PingTimeout;
+			break;
+		case p_i32_SendTimeout:
+			*Pint32_t(pValue)=SendTimeout;
+			break;
+		case p_i32_RecvTimeout:     
+			*Pint32_t(pValue)=RecvTimeout;
+			break;
+		case p_i32_WorkInterval:
+			*Pint32_t(pValue)=WorkInterval;
+			break;
+		case p_u16_SrcRef:
+			*Puint16_t(pValue)=SrcRef;
+			break;
+		case p_u16_DstRef:
+			*Puint16_t(pValue)=DstRef;
+			break;
+		case p_u16_SrcTSap:
+			*Puint16_t(pValue)=SrcTSap;
+			break;
+		case p_i32_PDURequest:
+			*Pint32_t(pValue)=PDURequest;
+			break;
+		case p_i32_BSendTimeout:
+			*Pint32_t(pValue)=BSendTimeout;
+			break;
+		case p_i32_BRecvTimeout:    
+			*Pint32_t(pValue)=BRecvTimeout;
+			break;
+		case p_u32_RecoveryTime:    
+			*Puint32_t(pValue)=RecoveryTime;
+			break;
+		case p_u32_KeepAliveTime:   
+			*Puint32_t(pValue)=KeepAliveTime;
+			break;
+		default: return errParInvalidParamNumber;
+	}
+	return 0;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::SetParam(int ParamNumber, void * pValue)
+{
+	switch (ParamNumber)
+	{
+		case p_u16_RemotePort:
+			if (!Connected && Active)
+				RemotePort=*Puint16_t(pValue);
+			else
+				return errParCannotChangeParam;
+			break;
+		case p_i32_PingTimeout:
+			PingTimeout=*Pint32_t(pValue);
+			break;
+		case p_i32_SendTimeout:
+			SendTimeout=*Pint32_t(pValue);
+			break;
+		case p_i32_RecvTimeout:     
+			RecvTimeout=*Pint32_t(pValue);
+			break;
+		case p_i32_WorkInterval:
+			WorkInterval=*Pint32_t(pValue);
+			break;
+		case p_u16_SrcRef:
+			SrcRef=*Puint16_t(pValue);
+			break;
+		case p_u16_DstRef:
+			DstRef=*Puint16_t(pValue);
+			break;
+		case p_u16_SrcTSap:
+			SrcTSap=*Puint16_t(pValue);
+			break;
+		case p_i32_PDURequest:
+			PDURequest=*Pint32_t(pValue);
+			break;
+		case p_i32_BSendTimeout:
+			BSendTimeout=*Pint32_t(pValue);
+			break;
+		case p_i32_BRecvTimeout:    
+			BRecvTimeout=*Pint32_t(pValue);
+			break;
+		case p_u32_RecoveryTime:    
+			RecoveryTime=*Puint32_t(pValue);
+			break;
+		case p_u32_KeepAliveTime:   
+			KeepAliveTime=*Puint32_t(pValue);
+			break;
+		default: return errParInvalidParamNumber;
+	}
+	return 0;
+}
+//------------------------------------------------------------------------------
+void TSnap7Partner::ClearRecv()
+{
+    memset(&FRecvStatus,0,sizeof(TRecvStatus));
+    FRecvPending=false;
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::ConnectToPeer()
+{
+    bool Result;
+    if (Active)
+    {
+        Result=PeerConnect()==0;  // try to Connect
+        Linked=Result;
+    }
+    else
+        Result =false;     // nothing : we are waiting for a connection
+
+    return Result;
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::PerformFunctionNegotiate()
+{
+    PReqFunNegotiateParams ReqParams;
+    PResFunNegotiateParams ResParams;
+    TS7Answer23 Answer;
+    int Size;
+
+    // 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;
+        return false;
+    };
+    // 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);
+    else
+        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()
+{
+     int Timeout;
+     if (FWorkerThread)
+     {
+          FWorkerThread->Terminate();
+          if (FRecvPending || FSendPending)
+             Timeout=3000;
+          else
+             Timeout=1000;
+
+          if (FWorkerThread->WaitFor(Timeout)!=WAIT_OBJECT_0)
+             FWorkerThread->Kill();
+          try {
+             delete FWorkerThread;
+          }
+          catch (...){
+          }
+          FWorkerThread=0;
+     }
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::BlockSend()
+{
+    PBSendReqParams ReqParams;
+    PBSendReqParams ResParams;
+    PBsendRequestData DataSendReq;
+    int TotalSize;
+    int SentSize;
+    int Slice;
+    int MaxSlice;
+    uintptr_t Offset;
+    pbyte Source;
+    bool First, Last;
+    byte Seq_IN;
+    int TxIsoSize;
+    pbyte Data;
+    pword TotalPackSize;
+    int DataPtrOffset;
+    word Extra;
+
+    ClrError();
+    TotalSize=TxBuffer.Size;
+    SentSize =TotalSize;
+    Offset=0;
+    First =true;
+    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;
+
+		if (Slice>MaxSlice)
+			Slice=MaxSlice;
+
+		TotalSize-=Slice;
+		Offset+=Slice;
+		Last=TotalSize==0;
+
+		// Prepare send
+		DataPtrOffset=sizeof(TS7ReqHeader)+sizeof(TBSendParams);
+		// Header
+		PDUH_out->P=0x32;                     // Always 0x32
+		PDUH_out->PDUType=PduType_userdata;  // 7
+		PDUH_out->AB_EX=0x0000;               // Always 0x0000
+		PDUH_out->Sequence=GetNextWord();      // Autoinc
+		PDUH_out->ParLen=SwapWord(sizeof(TBSendParams)); // 16 bytes
+
+		ReqParams->Head[0]=0x00;
+		ReqParams->Head[1]=0x01;
+		ReqParams->Head[2]=0x12;
+		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;
+		else
+			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;
+		else
+			ReqParams->IDSeq=GetNextByte();
+
+		DataSendReq=PBsendRequestData(pbyte(PDUH_out)+DataPtrOffset);
+		if (First)
+		{
+			// 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)
+		}
+		else
+		{
+			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;
+		}
+
+		if (First)
+		{
+			First =false;
+			MaxSlice+=2; // only in the first frame we have the extra info
+		};
+	};
+
+	SendTime=SysGetTick()-FSendElapsed;
+	if (LastError==0)
+		BytesSent+=SentSize;
+
+	return LastError==0;
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::PickData()
+{
+	PBSendReqParams   ReqParams;
+	PBSendReqParams   ResParams;
+	PBSendResData     ResData;
+	PBsendRequestData DataSendReq;
+	pbyte Source, Target;
+	pword TotalPackSize;
+	word Slice;
+	int AnswerLen;
+
+	ClrError();
+	// Setup pointers
+	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))
+	{
+		LastError=errParInvalidPDU;
+		return false;
+	}
+
+	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;
+	}
+	else {
+		Slice=SwapWord(DataSendReq->Len)-8;
+		Source=pbyte(DataSendReq)+sizeof(TBsendRequestData);
+	}
+
+	FRecvStatus.Done=ReqParams->EoS==0x00;
+
+	Target=pbyte(&RxBuffer)+FRecvStatus.Offset;
+	memcpy(Target, Source, Slice);
+	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);
+
+	return LastError==0;
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::BlockRecv()
+{
+	bool Result;
+    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;
+        RecvTime =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();
+    };
+    return Result;
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::ConnectionConfirm()
+{
+    if (FRecvPending)
+        ClearRecv();
+    IsoConfirmConnection(pdu_type_CC); // <- Connection confirm
+    return LastTcpError!=WSAECONNRESET;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::Status()
+{
+    if (Running)
+    {
+        if (Linked)
+        {
+            if (FRecvPending)
+                return par_receiving;
+            else
+                if (FSendPending)
+                    return par_sending;
+                else
+                    return par_linked;
+        }
+        else
+            if (Active)
+                return par_connecting;
+            else
+                return par_waiting;
+    }
+    else{
+        if ((!Active) && BindError)
+            return par_binderror;
+        else
+            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;
+    }
+
+	if (Destroying)
+		return false;
+
+    // Checks if there is something to recv
+    if (Result && CanRead(WorkInterval))
+    {
+        // Peeks info and returns PDU Kind
+        isoRecvPDU(&PDU);
+        if (LastTcpError==0)
+        {
+            // First check valid data incoming (most likely situation)
+            IsoPeek(&PDU,PduKind);
+            if (PduKind==pkValidData)
+            {
+                if (PDUH_in->PDUType==PduType_request)
+                {
+                    if (FRecvPending)
+                        ClearRecv();
+                    Result=PerformFunctionNegotiate();
+                }
+                else // Pdu type userdata
+                    Result=BlockRecv();
+            }
+            else
+                if (PduKind==pkConnectionRequest)
+                    Result=ConnectionConfirm();
+                else // nothing else
+                    Purge();
+        }
+        else
+            Result=false;
+    };
+
+    if (LastTcpError==WSAECONNRESET)
+    {
+        Result=false;
+        Linked=false;
+    }
+    else
+        if (!Result)
+            Disconnect();
+
+    // Check BRecv sequence timeout
+    RTimeout= FRecvPending && (SysGetTick()-FRecvStatus.Elapsed>longword(BRecvTimeout));
+
+    if (RTimeout)
+    {
+        LastError=errParFrameTimeout;
+        RecvEvt->Set();
+        if ((OnBRecv!=NULL) && !Destroying)
+            OnBRecv(FRecvUsrPtr, LastError, 0, &RxBuffer,0);
+    };
+
+  if (!Result || RTimeout)
+      ClearRecv();   // parframetimeout
+
+  return Result;
+}
+
+//------------------------------------------------------------------------------
+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);
+    if (Result==0)
+        Result=WaitAsBSendCompletion(BSendTimeout);
+    return Result;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::AsBSend(longword R_ID, void *pUsrData, int Size)
+{
+    SendTime=0;
+    if (Linked)
+    {
+      if (!FSendPending)
+      {
+          memcpy(&TxBuffer.Data, pUsrData, Size);
+          TxBuffer.R_ID=R_ID;
+          TxBuffer.Size=Size;
+          SendEvt->Reset();
+          FSendPending=true;
+          FSendElapsed=SysGetTick();
+          return 0;
+      }
+      else
+          return errParBusy;
+    }
+    else
+        return SetError(errParNotLinked);
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::CheckAsBSendCompletion(int &opResult)
+{
+    if (!Destroying)
+	{
+		if (!FSendPending)
+			opResult=LastError;
+		else
+			opResult=errParBusy;
+
+		return !FSendPending;
+	}
+	else
+	{
+		opResult=errParDestroying;
+		return true;
+    }
+
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::WaitAsBSendCompletion(longword Timeout)
+{
+   if (SendEvt->WaitFor(BSendTimeout)==WAIT_OBJECT_0)
+   {
+		if (!Destroying)
+			return LastError;
+		else
+			return SetError(errParDestroying);
+   }
+   else
+       return SetError(errParSendTimeout);
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::SetSendCallback(pfn_ParBSendCompletion pCompletion, void *usrPtr)
+{
+    OnBSend=pCompletion;
+    FSendUsrPtr=usrPtr;
+    return 0;
+}
+//------------------------------------------------------------------------------
+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);
+             else
+                 Result=errParInvalidParams;
+         }
+         else
+             Result=FRecvLast.Result;
+         RecvEvt->Reset();
+     }
+     else
+         Result=errParRecvTimeout;
+
+     return SetError(Result);
+}
+//------------------------------------------------------------------------------
+bool TSnap7Partner::CheckAsBRecvCompletion(int &opResult, longword &R_ID,
+    void *pData, int &Size)
+{
+    if (Destroying)
+	{
+		Size=0;
+		opResult=errParDestroying;
+		return true;
+	}
+	
+	bool Result=FRecvLast.Done;
+    if (Result)
+    {
+        Size=FRecvLast.Size;
+        R_ID=FRecvLast.R_ID;
+        opResult=FRecvLast.Result;
+        if ((pData!=NULL) && (Size>0) && (opResult==0))
+           memcpy(pData, &RxBuffer, Size);
+        FRecvLast.Done=false;
+    }
+    return Result;
+}
+//------------------------------------------------------------------------------
+int TSnap7Partner::SetRecvCallback(pfn_ParBRecvCallBack pCompletion, void *usrPtr)
+{
+    OnBRecv=pCompletion;
+    FRecvUsrPtr=usrPtr;
+    return 0;
+}
+//------------------------------------------------------------------------------
+

+ 284 - 0
src/core/s7_partner.h

@@ -0,0 +1,284 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_partner_h
+#define s7_partner_h
+//---------------------------------------------------------------------------
+#include "snap_threads.h"
+#include "s7_peer.h"
+//---------------------------------------------------------------------------
+using namespace std;
+//---------------------------------------------------------------------------
+
+#define MaxPartners 256
+#define MaxAdapters 256
+#define csTimeout   1500 // Connection server destruction timeout
+
+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
+const int par_sending         = 4;   // sending data
+const int par_receiving       = 5;   // receiving data
+const int par_binderror       = 6;   // error starting passive partner
+
+const longword errParMask               = 0xFFF00000;
+const longword errParBase               = 0x000FFFFF;
+
+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
+
+class TSnap7Partner;
+typedef TSnap7Partner *PSnap7Partner;
+
+class TConnectionServer;
+typedef TConnectionServer *PConnectionServer;
+
+//------------------------------------------------------------------------------
+// CONNECTION SERVERS MANAGER
+//------------------------------------------------------------------------------
+class TServersManager
+{
+private:
+    PConnectionServer Servers[MaxAdapters];
+    TSnapCriticalSection *cs;
+    void Lock();
+    void Unlock();
+    int CreateServer(longword BindAddress, PConnectionServer &Server);
+    void AddServer(PConnectionServer Server);
+public:
+    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
+{
+private:
+	TMsgSocket *FListener;
+	TConnectionServer *FServer;
+public:
+	TConnListenerThread(TMsgSocket *Listener, TConnectionServer *Server)
+    {
+        FServer=Server;
+        FListener=Listener;
+        FreeOnTerminate=false;
+    };
+	void Execute();
+};
+typedef TConnListenerThread *PConnListenerThread;
+
+
+class TConnectionServer
+{
+private:
+    TSnapCriticalSection *cs;
+    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
+    void Lock();
+    // Unlocks the Partner list
+    void Unlock();
+protected:
+	// Workers list
+	PSnap7Partner Partners[MaxPartners];
+    bool Destroying;
+    void Incoming(socket_t Sock);
+    int Start();
+    int FirstFree();
+public:
+    int PartnersCount;
+    longword LocalBind;
+    TConnectionServer();
+    ~TConnectionServer();
+
+    int StartTo(const char *Address);
+    void Stop();
+    int RegisterPartner(PSnap7Partner Partner);
+    void RemovePartner(PSnap7Partner Partner);
+    friend class TConnListenerThread;
+};
+typedef TConnectionServer * PConnectionServer;
+
+//------------------------------------------------------------------------------
+// PARTNER THREAD
+//------------------------------------------------------------------------------
+class TPartnerThread : public TSnapThread
+{
+private:
+    TSnap7Partner *FPartner;
+    longword FRecoveryTime;
+    longword FKaElapsed;
+protected:
+    void Execute();
+public:
+    TPartnerThread(TSnap7Partner *Partner, longword RecoveryTime)
+    {
+        FPartner = Partner;
+        FRecoveryTime =RecoveryTime;
+        FreeOnTerminate =false;
+    };
+    ~TPartnerThread(){};
+};
+typedef TPartnerThread *PPartnerThread;
+//------------------------------------------------------------------------------
+// S7 PARTNER
+//------------------------------------------------------------------------------
+
+typedef struct{
+    bool      First;
+    bool      Done;
+    uintptr_t Offset;
+    longword  TotalLength;
+    longword  In_R_ID;
+    longword  Elapsed;
+    byte      Seq_Out;
+}TRecvStatus;
+
+typedef struct{
+    bool     Done;
+    int      Size;
+    int      Result;
+    longword R_ID;
+    longword Count;
+}TRecvLast;
+
+extern "C" {
+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
+{
+private:
+    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();
+protected:
+    bool Stopping;
+    bool Execute();
+    void Disconnect();
+    bool ConnectToPeer();
+    bool PerformFunctionNegotiate();
+public:
+    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();
+    // Control
+    int Start();
+    int StartTo(const char *LocAddress, const char *RemAddress, word LocTsap, word RemTsap);
+    int Stop();
+    int Status();
+	int GetParam(int ParamNumber, void * pValue);
+	int SetParam(int ParamNumber, void * pValue);
+    // Block send
+    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_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

+ 122 - 0
src/core/s7_peer.cpp

@@ -0,0 +1,122 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "s7_peer.h"
+//---------------------------------------------------------------------------
+
+TSnap7Peer::TSnap7Peer()
+{
+    PDUH_out=PS7ReqHeader(&PDU.Payload);
+    PDURequest=480; // Our request, FPDULength will contain the CPU answer
+    LastError=0;
+	cntword = 0;
+    Destroying = false;
+}
+//---------------------------------------------------------------------------
+TSnap7Peer::~TSnap7Peer()
+{
+    Destroying = true;
+}
+//---------------------------------------------------------------------------
+int TSnap7Peer::SetError(int Error)
+{
+    if (Error==0)
+       ClrError();
+    else
+       LastError=Error | LastIsoError | LastTcpError;
+    return Error;
+}
+//---------------------------------------------------------------------------
+void TSnap7Peer::ClrError()
+{
+    LastError=0;
+    LastIsoError=0;
+    LastTcpError=0;
+}
+//---------------------------------------------------------------------------
+word TSnap7Peer::GetNextWord()
+{
+     if (cntword==0xFFFF)
+        cntword=0;
+     return cntword++;
+}
+//---------------------------------------------------------------------------
+int TSnap7Peer::NegotiatePDULength( )
+{
+    int Result, IsoSize = 0;
+    PReqFunNegotiateParams ReqNegotiate;
+    PResFunNegotiateParams ResNegotiate;
+    PS7ResHeader23 Answer;
+    ClrError();
+    // Setup Pointers
+    ReqNegotiate = PReqFunNegotiateParams(pbyte(PDUH_out) + sizeof(TS7ReqHeader));
+    // Header
+    PDUH_out->P        = 0x32;            // Always $32
+    PDUH_out->PDUType  = PduType_request; // $01
+    PDUH_out->AB_EX    = 0x0000;          // Always $0000
+    PDUH_out->Sequence = GetNextWord();   // AutoInc
+    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))))
+    {
+        // Setup pointers
+        Answer = PS7ResHeader23(&PDU.Payload);
+        ResNegotiate = PResFunNegotiateParams(pbyte(Answer) + sizeof(TS7ResHeader23));
+        if ( Answer->Error != 0 )
+	    Result = SetError(errNegotiatingPDU);
+        if ( Result == 0 )
+	    PDULength = SwapWord(ResNegotiate->PDULength);
+    }
+    return Result;
+}
+//---------------------------------------------------------------------------
+void TSnap7Peer::PeerDisconnect( )
+{
+    ClrError();
+    isoDisconnect(true);
+}
+//---------------------------------------------------------------------------
+int TSnap7Peer::PeerConnect( )
+{
+    int Result;
+
+    ClrError();
+	Result = isoConnect();
+	if (Result == 0)
+	{
+		Result = NegotiatePDULength();
+		if (Result != 0)
+			PeerDisconnect();
+	}
+    return Result;
+}

+ 58 - 0
src/core/s7_peer.h

@@ -0,0 +1,58 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_peer_h
+#define s7_peer_h
+//---------------------------------------------------------------------------
+#include "s7_types.h"
+#include "s7_isotcp.h"
+//---------------------------------------------------------------------------
+
+const longword errPeerMask 	     = 0xFFF00000;
+const longword errPeerBase       = 0x000FFFFF;
+const longword errNegotiatingPDU = 0x00100000;
+
+class TSnap7Peer: public TIsoTcpSocket
+{
+private:
+    word cntword;
+protected:
+    bool Destroying;
+    PS7ReqHeader PDUH_out;
+    word GetNextWord();
+    int SetError(int Error);
+    int NegotiatePDULength();
+    void ClrError();
+public:
+    int LastError;
+    int PDULength;
+    int PDURequest;
+    TSnap7Peer();
+    ~TSnap7Peer();
+    void PeerDisconnect();
+    int PeerConnect();
+};
+//---------------------------------------------------------------------------
+#endif

+ 2159 - 0
src/core/s7_server.cpp

@@ -0,0 +1,2159 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "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)
+{
+    return true;
+}
+//---------------------------------------------------------------------------
+bool TIsoTcpWorker::ExecuteSend()
+{
+    return true;
+}
+//---------------------------------------------------------------------------
+bool TIsoTcpWorker::ExecuteRecv()
+{
+    TPDUKind PduKind;
+    int PayloadSize;
+
+    if (CanRead(WorkInterval)) // should be Small to avoid time wait during the close
+    {
+        isoRecvPDU(&PDU);
+        if (LastTcpError==0)
+        {
+            IsoPeek(&PDU,PduKind);
+            // First check valid data incoming (most likely situation)
+            if (PduKind==pkValidData)
+            {
+                PayloadSize=PDUSize(&PDU)-DataHeaderSize;
+                return IsoPerformCommand(PayloadSize);
+            };
+            // Connection request incoming
+            if (PduKind==pkConnectionRequest)
+            {
+                IsoConfirmConnection(pdu_type_CC); // <- Connection confirm
+                return LastTcpError!=WSAECONNRESET;
+            };
+            // Disconnect request incoming (only for isotcp full complient equipment, not S7)
+            if (PduKind==pkDisconnectRequest)
+            {
+                IsoConfirmConnection(pdu_type_DC); // <- Disconnect confirm
+                return false;
+            };
+            // Empty fragment, maybe an ACK
+            if (PduKind==pkEmptyFragment)
+            {
+                PayloadSize=0;
+                return IsoPerformCommand(PayloadSize);
+            };
+            // Valid PDU format but we have to discard it
+            if (PduKind==pkUnrecognizedType)
+            {
+                return LastTcpError!=WSAECONNRESET;
+            };
+            // Here we have an Invalid PDU
+            Purge();
+            return true;
+        }
+        else
+            return LastTcpError!=WSAECONNRESET;
+    }
+    else
+        return true;
+}
+//---------------------------------------------------------------------------
+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)
+        return false;
+    // Checks PDUType : must be 1 or 7
+    if ((PDUH_in->PDUType!=PduType_request) &&
+        (PDUH_in->PDUType!=PduType_userdata))
+        return false;
+    else
+        return true;
+}
+//------------------------------------------------------------------------------
+byte TS7Worker::BCD(word Value)
+{
+    return ((Value / 10) << 4) + (Value % 10);
+}
+//------------------------------------------------------------------------------
+void TS7Worker::FillTime(PS7Time PTime)
+{
+    time_t Now;
+    time(&Now);
+    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,
+  word Param3, word Param4)
+{
+    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);
+        }
+    }
+    else
+        DoEvent(evcPDUincoming, evrMalformedPDU, Size, 0, 0, 0);
+    return false;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformPDUAck(int &Size)
+{
+    // here we could keep track of ack empty fragment for debug purpose
+    return true;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformPDURequest(int &Size)
+{
+    pbyte P;
+    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();
+             break;
+        case pduFuncWrite   : Result=PerformFunctionWrite();
+             break;
+		case pduNegotiate   : Result=PerformFunctionNegotiate();
+             break;
+        case pduStart       :
+        case pduStop        : Result=PerformFunctionControl(PDUFun);
+             break;
+		case pduStartUpload :
+        case pduUpload      :
+        case pduEndUpload   : Result=PerformFunctionUpload();
+             break;
+        case pduReqDownload : Result=PerformFunctionDownload();
+             break;
+        // <-- Further (custom) functions can be added here
+        default:
+             DoEvent(evcPDUincoming, evrCannotHandlePDU, Size, 0, 0, 0);
+    };
+    return Result;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformPDUUsrData(int &Size)
+{
+    PS7ReqParams7 ReqParams;
+    byte Tg, SubFun;
+    bool Result = true;
+    // 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();
+          break;
+      case grCyclicData : Result=PerformGroupCyclicData();
+          break;
+      case grBlocksInfo : Result=PerformGroupBlockInfo();
+          break;
+      case grSZL        : Result=PerformGroupSZL();
+          break;
+      case grPassword   : Result=PerformGroupSecurity();
+          break;
+      case grClock      : switch (SubFun)
+                         {
+                             case 0x01 : Result=PerformGetClock();
+                                 break;
+                             case 0x02 : Result=PerformSetClock();
+                                 break;
+                         };
+                         break;
+      default:
+          DoEvent(evcPDUincoming, evrInvalidGroupUData, Tg, 0, 0, 0);
+    };
+    return Result;
+}
+//------------------------------------------------------------------------------
+int TS7Worker::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;
+     }
+}
+//==============================================================================
+// FUNCTION READ
+//==============================================================================
+word TS7Worker::RA_NotFound(PResFunReadItem ResItem, TEv &EV)
+{
+    ResItem->DataLength=SwapWord(0x0004);
+    ResItem->ReturnCode=Code7ResItemNotAvailable;
+    ResItem->TransportSize=0x00;
+    EV.EvRetCode=evrErrAreaNotFound;
+    return 0;
+}
+//------------------------------------------------------------------------------
+word TS7Worker::RA_OutOfRange(PResFunReadItem ResItem, TEv &EV)
+{
+    ResItem->DataLength=SwapWord(0x0004);
+    ResItem->ReturnCode=Code7AddressOutOfRange;
+    ResItem->TransportSize=0x00;
+    EV.EvRetCode=evrErrOutOfRange;
+    return 0;
+}
+//------------------------------------------------------------------------------
+word TS7Worker::RA_SizeOverPDU(PResFunReadItem ResItem, TEv &EV)
+{
+    ResItem->DataLength=SwapWord(0x0004);
+    ResItem->ReturnCode=byte(SwapWord(Code7DataOverPDU));
+    ResItem->TransportSize=0x00;
+    EV.EvRetCode=evrErrOverPDU;
+    return 0;
+}
+//------------------------------------------------------------------------------
+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))
+        return RA_OutOfRange(ResItemData, EV);
+
+    if ((ReqItemPar->Area==S7AreaCT) ^ (ReqItemPar->TransportSize==S7WLCounter))
+        return RA_OutOfRange(ResItemData, EV);
+
+    // 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);
+    else
+        PDURemainder-=Size;
+
+    // More then 1 bit is not supported by S7 CPU 
+    if ((ReqItemPar->TransportSize==S7WLBit) && (Size>1))
+        return RA_OutOfRange(ResItemData, EV);
+
+	// 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)
+	   )
+		return RA_OutOfRange(ResItemData, EV);
+
+    // AStart is only for callback
+	if ((ReqItemPar->TransportSize != S7WLBit) && (ReqItemPar->TransportSize != S7WLCounter) && (ReqItemPar->TransportSize != S7WLTimer))
+		AStart = Start >> 3;
+	else
+		AStart = Start;
+
+	if ((ReqItemPar->TransportSize == S7WLCounter) || (ReqItemPar->TransportSize == S7WLTimer))
+	{
+		Start = Start >> 1;   // 1 Timer or Counter = 2 bytes
+	}
+	else
+	{
+		BitIndex  =Start & 0x07; // start bit
+		Start     =Start >> 3;   // start byte
+	}
+	
+	EV.EvStart=Start;
+
+	// Checks bounds
+	if (!FServer->ResourceLess)
+	{
+		ASize = P->Size; // Area size
+		if (Start + Size > ASize)
+			return RA_OutOfRange(ResItemData, EV);
+		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))
+			return RA_NotFound(ResItemData, EV);
+	}
+	else
+	{
+		// Lock the area
+		pcs = P->cs;
+		pcs->Enter();
+		// Get Data
+		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)
+    {
+      case S7WLBit:
+        {
+          ByteVal=ResItemData->Data[0];
+
+          if ((ByteVal & BitMask[BitIndex])!=0)
+              ResItemData->Data[0]=0x01;
+          else
+              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);
+        };break;
+	  case S7WLInt:
+	  case S7WLDInt:
+        {
+          ResItemData->TransportSize=TS_ResInt;
+          ResItemData->DataLength=SwapWord(Size*8);
+        };break;
+      case S7WLReal:
+        {
+          ResItemData->TransportSize=TS_ResReal;
+          ResItemData->DataLength=SwapWord(Size);
+        };break;
+      case S7WLChar:
+      case S7WLTimer:
+      case S7WLCounter:
+        {
+          ResItemData->TransportSize=TS_ResOctet;
+		  ResItemData->DataLength=SwapWord(Size);
+        };break;
+      default : 
+        {
+          ResItemData->TransportSize=TS_ResByte;
+          ResItemData->DataLength=SwapWord(Size*8);
+        };break;
+    }
+    EV.EvRetCode=evrNoError;
+    return Size;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformFunctionRead()
+{
+    PReqFunReadParams ReqParams;
+    PResFunReadParams ResParams;
+    TResFunReadData   ResData;
+    TS7Answer23       Answer;
+    uintptr_t         Offset;
+    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
+
+    for (c = 0; c < ItemsCount; c++)
+	{
+		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.P=0x32;
+    Answer.Header.PDUType=0x03;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    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)
+        DoEvent(evcDataRead,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+
+    return true;
+}
+//==============================================================================
+// FUNCTION WRITE
+//==============================================================================
+byte TS7Worker::WA_NotFound(TEv &EV)
+{
+	EV.EvRetCode=evrErrAreaNotFound;
+    return Code7ResItemNotAvailable;
+}
+//------------------------------------------------------------------------------
+byte TS7Worker::WA_InvalidTransportSize(TEv &EV)
+{
+    EV.EvRetCode=evrErrTransportSize;
+    return Code7InvalidTransportSize;
+}
+//------------------------------------------------------------------------------
+byte TS7Worker::WA_OutOfRange(TEv &EV)
+{
+    EV.EvRetCode=evrErrOutOfRange;
+    return Code7AddressOutOfRange;
+}
+//------------------------------------------------------------------------------
+byte TS7Worker::WA_DataSizeMismatch(TEv &EV)
+{
+    EV.EvRetCode=evrDataSizeMismatch;
+    return Code7WriteDataSizeMismatch;
+}
+//------------------------------------------------------------------------------
+byte TS7Worker::WriteArea(PReqFunWriteDataItem ReqItemData, PReqFunWriteItem ReqItemPar,
+     TEv &EV)
+{
+	int Multiplier;
+    PS7Area P = NULL;
+	word DBNum = 0;
+	word Elements;
+    longword *PAdd;
+	PSnapCriticalSection pcs;
+	longword Start, Size, ASize, DataLen, AStart;
+	pbyte Target = NULL;
+	byte BitIndex;
+
+    EV.EvStart   =0;
+	EV.EvSize    =0;
+    EV.EvRetCode =evrNoError;
+    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 WA_NotFound(EV);
+	}
+
+    // Calcs the amount
+	Multiplier = DataSizeByte(ReqItemPar->TransportSize);
+	if (Multiplier==0)
+		return WA_InvalidTransportSize(EV);
+
+    // Checks timers/counters coherence
+    if ((ReqItemPar->Area==S7AreaTM) ^ (ReqItemPar->TransportSize==S7WLTimer))
+        return WA_OutOfRange(EV);
+
+	if ((ReqItemPar->Area==S7AreaCT) ^ (ReqItemPar->TransportSize==S7WLCounter))
+        return WA_OutOfRange(EV);
+
+	// Calcs size
+	Elements = SwapWord(ReqItemPar->Length);
+	Size = Multiplier*Elements;
+    EV.EvSize=Size;
+
+    // More) 1 bit is not supported by S7 CPU
+    if ((ReqItemPar->TransportSize==S7WLBit) && (Size>1))
+        return WA_OutOfRange(EV);
+
+    // 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)
+	   )
+		return WA_OutOfRange(EV);
+
+	// AStart is only for callback
+	if ((ReqItemPar->TransportSize != S7WLBit) && (ReqItemPar->TransportSize != S7WLCounter) && (ReqItemPar->TransportSize != S7WLTimer))
+		AStart = Start >> 3;
+	else
+		AStart = Start;
+
+	if ((ReqItemPar->TransportSize == S7WLCounter) || (ReqItemPar->TransportSize == S7WLTimer))
+	{
+		Start = Start >> 1;   // 1 Timer or Counter = 2 bytes
+	}
+	else
+	{
+		BitIndex = Start & 0x07; // start bit
+		Start = Start >> 3;   // start byte
+	}
+	EV.EvStart =Start;
+	
+	if (!FServer->ResourceLess)
+	{
+		// Checks bounds
+		ASize = P->Size; // Area size
+		if (Start + Size > ASize)
+			return WA_OutOfRange(EV);
+		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->ResourceLess)
+	{
+		if (!FServer->DoWriteArea(ClientHandle, EV.EvArea, EV.EvIndex, AStart, Elements, ReqItemPar->TransportSize, &ReqItemData->Data[0]))
+			return WA_NotFound(EV);
+	}
+	else
+	{
+		if (ReqItemPar->TransportSize==S7WLBit)
+		{
+		  if ((ReqItemData->Data[0] & 0x01) != 0)   // bit set
+			  *Target=*Target | BitMask[BitIndex];
+		  else                                      // bit reset
+			  *Target=*Target & (~BitMask[BitIndex]);
+		}
+		else {
+		  // Lock the area
+			pcs = P->cs;
+			pcs->Enter();
+			// Write Data
+			memcpy(Target, &ReqItemData->Data[0], Size);
+			pcs->Leave();
+		};
+	}
+	
+	return 0xFF;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformFunctionWrite()
+{
+    PReqFunWriteParams ReqParams;
+    TReqFunWriteData ReqData;
+    PResFunWrite ResData;
+    TS7Answer23 Answer;
+	int L;
+
+	uintptr_t StartData;
+	int c, ItemsCount;
+	int ResDSize;
+	TEv EV;
+
+	// Stage 1 : Setup pointers and initial check
+	ReqParams=PReqFunWriteParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+	ResData  =PResFunWrite(pbyte(&Answer)+ResHeaderSize23);
+
+	StartData=sizeof(TS7ReqHeader)+SwapWord(PDUH_in->ParLen);
+
+	ItemsCount=ReqParams->ItemsCount;
+	ResDSize  =ResHeaderSize23+2+ItemsCount;
+	for (c = 0; c < ItemsCount; c++)
+	{
+		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);
+		else
+			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
+	for (c = 0; c < ItemsCount; c++)
+	{
+	  ResData->Data[c]=WriteArea(ReqData[c],&ReqParams->Items[c], EV);
+      // For multiple items we have to create multiple events
+      if (ItemsCount>1)
+           DoEvent(evcDataWrite,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+    }
+
+    // Stage 3 : finalize 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(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
+    // we sent the answer
+    if (ItemsCount==1)
+        DoEvent(evcDataWrite,EV.EvRetCode,EV.EvArea,EV.EvIndex,EV.EvStart,EV.EvSize);
+    return true;
+}
+//==============================================================================
+// FUNCTION NEGOTIATE
+//==============================================================================
+bool TS7Worker::PerformFunctionNegotiate()
+{
+	PReqFunNegotiateParams ReqParams;
+	PResFunNegotiateParams ResParams;
+	word ReqLen;
+	TS7Answer23 Answer;
+	int Size;
+
+	// Setup pointers
+	ReqParams=PReqFunNegotiateParams(pbyte(PDUH_in)+sizeof(TS7ReqHeader));
+	ResParams=PResFunNegotiateParams(pbyte(&Answer)+sizeof(TS7ResHeader23));
+	// 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 end of the header
+	ResParams->FunNegotiate=pduNegotiate;
+	ResParams->Unknown=0x0;
+	// We offer the same
+	ResParams->ParallelJobs_1=ReqParams->ParallelJobs_1;
+	ResParams->ParallelJobs_2=ReqParams->ParallelJobs_2;
+
+	if (FServer->ForcePDU == 0)
+	{
+		ReqLen = SwapWord(ReqParams->PDULength);
+		if (ReqLen<MinPduSize)
+			ResParams->PDULength = SwapWord(MinPduSize);
+		else
+			if (ReqLen>IsoPayload_Size)
+				ResParams->PDULength = SwapWord(IsoPayload_Size);
+			else
+				ResParams->PDULength = ReqParams->PDULength;
+	}
+	else
+		ResParams->PDULength = SwapWord(FServer->ForcePDU);
+
+	FPDULength=SwapWord(ResParams->PDULength); // Stores the value
+	// Sends the answer
+	Size=sizeof(TS7ResHeader23) + sizeof(TResFunNegotiateParams);
+	isoSendBuffer(&Answer, Size);
+	// Store the event
+	DoEvent(evcNegotiatePDU, evrNoError, FPDULength, 0, 0, 0);
+	return true;
+}
+//==============================================================================
+// FUNCTION CONTROL
+//==============================================================================
+bool TS7Worker::PerformFunctionControl(byte PduFun)
+{
+    TS7Answer23 Answer;
+    PResFunCtrl ResParams;
+    word ParLen;
+    word CtrlCode;
+
+    // Setup pointer
+    ResParams=PResFunCtrl(pbyte(&Answer)+sizeof(TS7ResHeader23));
+    // 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(0x0001); // We send only Res fun without para
+    Answer.Header.DataLen=0x0000;
+    Answer.Header.Error=0x0000;
+    ResParams->ResFun=PduFun;
+    ResParams->para  =0x00;
+
+    ParLen=SwapWord(PDUH_in->ParLen);
+    if (PduFun==pduStop)
+      CtrlCode=CodeControlStop;
+    else
+    {
+      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;
+      }
+    }
+    // Sends the answer
+    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;
+
+    return true;
+}
+//==============================================================================
+// FUNCTION UPLOAD
+//==============================================================================
+bool TS7Worker::PerformFunctionUpload()
+{
+    TS7Answer23 Answer;
+
+    // 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
+
+    // Prepares the answer
+    Answer.Header.P=0x32;
+    Answer.Header.PDUType =pduResponse;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    Answer.Header.ParLen=0;
+    Answer.Header.DataLen=0;
+    Answer.Header.Error=SwapWord(Code7NeedPassword);
+    // Sends the answer
+    isoSendBuffer(&Answer,sizeof(TS7ResHeader23));
+
+    DoEvent(evcUpload,evrCannotUpload,evsStartUpload,0,0,0);
+    return true;
+}
+//==============================================================================
+// FUNCTION DOWNLOAD
+//==============================================================================
+bool TS7Worker::PerformFunctionDownload()
+{
+    TS7Answer23 Answer;
+
+    // Download 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
+
+    // Prepares the answer
+    Answer.Header.P=0x32;
+    Answer.Header.PDUType =pduResponse;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    Answer.Header.ParLen=0;
+    Answer.Header.DataLen=0;
+    Answer.Header.Error=SwapWord(Code7NeedPassword);
+    // Sends the answer
+    isoSendBuffer(&Answer,sizeof(TS7ResHeader23));
+
+    DoEvent(evcUpload,evrCannotDownload, evsStartDownload,0,0,0);
+    return true;
+}
+//==============================================================================
+// FUNCTIONS PROGRAMMER AND CYCLIC DATA (NOT IMPLEMENTED...yet)
+//==============================================================================
+bool TS7Worker::PerformGroupProgrammer()
+{
+    DoEvent(evcPDUincoming,evrNotImplemented,grProgrammer,0,0,0);
+    return true;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformGroupCyclicData()
+{
+    DoEvent(evcPDUincoming,evrNotImplemented,grCyclicData,0,0,0);
+    return true;
+}
+//==============================================================================
+// BLOCK(S) INFO FUNCTIONS
+//==============================================================================
+void TS7Worker::BLK_ListAll(TCB &CB)
+{
+    PDataFunListAll Data;
+    int TotalSize;
+
+    TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+sizeof(TDataFunListAll);
+    // Prepares the answer
+    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;
+    // Prepares the answer
+    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.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_ListBoT;
+    CB.ResParams->Seq   =CB.ReqParams->Seq;
+    CB.ResParams->Rsvd  =0x0000;
+    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;
+			      cnt++;
+		      };
+	      };
+
+	      if ((cnt<MaxItems) || (DBCnt==HiBound))
+	      {
+		      DBCnt=0; // Finished
+		      CB.ResParams->Rsvd=0x0023;
+	      }
+	      else
+		      CB.ResParams->Rsvd=0x0123;
+
+	      if (cnt>0)
+	      {
+		      CB.ResParams->ErrNo =0x0000;
+		      Data->TSize =TS_ResOctet;
+		      Data->RetVal=0xFF;
+		      CB.DataLength=4+(cnt*word(sizeof(TDataFunGetBotItem)));
+		      CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
+		      Data->DataLen=SwapWord(CB.DataLength-4);
+	      }
+	      else
+		      BLK_NoResource_ListBoT(Data, CB);
+      }
+      else
+	      BLK_NoResource_ListBoT(Data, CB);
+    }
+    else // we store only DBs
+	    BLK_NoResource_ListBoT(Data, CB);
+
+    TotalSize = ResHeaderSize17+sizeof(TResFunGetBlockInfo)+CB.DataLength;
+    isoSendBuffer(&CB.Answer,TotalSize);
+    if (Start)
+        DoEvent(evcDirectory, CB.evError, evsStartListBoT, BlockType, 0, 0);
+    else
+        DoEvent(evcDirectory, CB.evError, evsListBoT, BlockType, 0, 0);
+}
+//------------------------------------------------------------------------------
+void TS7Worker::BLK_NoResource_GetBlkInfo(PResDataBlockInfo Data, TCB &CB)
+{
+    CB.DataLength  =4;
+    CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
+    CB.ResParams->ErrNo =0x09D2; // function in error
+    Data->RetVal        =0x0A;   // No resource available
+    Data->TSize         =0x00;   // No transport size;
+    Data->Length        =0x0000; // No data;
+    CB.evError          =evrResNotFound;
+}
+//------------------------------------------------------------------------------
+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)
+{
+    // Prepares the answer
+    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.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_BlkInfo;
+    CB.ResParams->Seq   =CB.ReqParams->Seq;
+    CB.ResParams->Rsvd  =0x0000;
+    CB.ResParams->ErrNo =0x0000;
+
+    CB.DataLength =sizeof(TResDataBlockInfo);
+    CB.Answer.Header.DataLen=SwapWord(CB.DataLength);
+    CB.ResParams->ErrNo =0x0000;
+
+    Data->RetVal       =0xFF;
+    Data->TSize        =TS_ResOctet;
+    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)
+{
+    PReqDataBlockInfo ReqData;
+    PResDataBlockInfo Data;
+    int BlkNum;
+    PS7Area BlkDB;
+    byte BlkTypeInfo;
+    int TotalSize;
+
+    CB.evError=0;
+    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);
+	    else
+                BLK_NoResource_GetBlkInfo(Data, CB);
+      }
+      else
+          BLK_NoResource_GetBlkInfo(Data, CB);
+    }
+    else
+        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;
+
+    // Setup pointers
+    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
+          }
+          else
+              BLK_ListBoT(LastBlk, false, CB);  // Continue sequence
+      }; break;
+      case SFun_BlkInfo : BLK_GetBlkInfo(CB); // Get Block info
+    }
+    return true;
+}
+//==============================================================================
+// 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->Err  =0x0000;
+	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);
+	SZL.SZLDone=true;
+}
+// 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));
+	SZL.ResParams->Err  =0x0000;
+	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;
+	SZL.SZLDone=true;
+	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);
+	SZL.ResParams->Err  =0x0000;
+	SZL.ResParams->resvd=0x0000; // this is the end, no more packets
+	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;
+
+	isoSendBuffer(&SZL.Answer,22+len);
+	SZL.SZLDone=true;
+}
+
+bool TS7Worker::PerformGroupSZL()
+{
+  SZL.SZLDone=false;
+  // Setup pointers
+  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;
+  SZL.ResParams->resvd=0x0000; // this is the end, no more packets
+
+  // only two subfunction are defined : 0x01 read, 0x02 system state
+  if (SZL.ResParams->SubFun==0x02)   // 0x02 = subfunction system state
+  {
+      SZLSystemState();
+      return true;
+  };
+  if (SZL.ResParams->SubFun!=0x01)
+  {
+      SZLNotAvailable();
+      return true;
+  };
+  // 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;
+                  };
+                  break;
+    case 0x0092 : switch(SZL.Index){
+                    case 0x0000 : SZLData(&SZL_ID_0092_IDX_0000,sizeof(SZL_ID_0092_IDX_0000));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0292 : switch(SZL.Index){
+                    case 0x0000 : SZLData(&SZL_ID_0292_IDX_0000,sizeof(SZL_ID_0292_IDX_0000));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0692 : switch(SZL.Index){
+                    case 0x0000 : SZLData(&SZL_ID_0692_IDX_0000,sizeof(SZL_ID_0692_IDX_0000));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+	case 0x0094 : switch(SZL.Index){
+                    case 0x0000 : SZLData(&SZL_ID_0094_IDX_0000,sizeof(SZL_ID_0094_IDX_0000));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0D97 : switch(SZL.Index){
+                    case 0x0000 : SZLData(&SZL_ID_0D97_IDX_0000,sizeof(SZL_ID_0D97_IDX_0000));break;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                   default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0113 : switch(SZL.Index){
+                    case 0x0001 : SZLData(&SZL_ID_0113_IDX_0001,sizeof(SZL_ID_0113_IDX_0001));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+	case 0x0115 : switch(SZL.Index){
+                    case 0x0800 : SZLData(&SZL_ID_0115_IDX_0800,sizeof(SZL_ID_0115_IDX_0800));break;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0225 : switch(SZL.Index){
+                    case 0x0001 : SZLData(&SZL_ID_0225_IDX_0001,sizeof(SZL_ID_0225_IDX_0001));break;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                     default     : SZLNotAvailable();break;
+                   };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;
+                     default     : SZLNotAvailable();break;
+                   };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;
+                     default     : SZLNotAvailable();break;
+                   };break;
+    case 0x0137 : switch(SZL.Index){
+                    case 0x07FE : SZLData(&SZL_ID_0137_IDX_07FE,sizeof(SZL_ID_0137_IDX_07FE));break;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                     default     : SZLNotAvailable();break;
+                   };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;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0194 : switch(SZL.Index){
+                    case 0x0064 : SZLData(&SZL_ID_0194_IDX_0064,sizeof(SZL_ID_0194_IDX_0064));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    case 0x0694 : switch(SZL.Index){
+                    case 0x0064 : SZLData(&SZL_ID_0694_IDX_0064,sizeof(SZL_ID_0694_IDX_0064));break;
+                    default     : SZLNotAvailable();break;
+                  };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;
+                     default     : SZLNotAvailable();break;
+                   };break;
+    case 0x0C91 : switch(SZL.Index){
+                    case 0x07FE : SZLData(&SZL_ID_0C91_IDX_07FE,sizeof(SZL_ID_0C91_IDX_07FE));break;
+                    default     : SZLNotAvailable();break;
+                  };break;
+    default : SZLNotAvailable();break;
+  }
+  // Event
+  if (SZL.SZLDone)
+      DoEvent(evcReadSZL,evrNoError,SZL.ID,SZL.Index,0,0);
+  else
+      DoEvent(evcReadSZL,evrInvalidSZL,SZL.ID,SZL.Index,0,0);
+  return true;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformGroupSecurity()
+{
+    PReqFunSecurity ReqParams;
+    PResParamsSecurity ResParams;
+    PResDataSecurity ResData;
+    TS7Answer17 Answer;
+    int TotalSize;
+
+    ReqParams=PReqFunSecurity(pbyte(PDUH_in)+ReqHeaderSize);
+    ResParams=PResParamsSecurity(pbyte(&Answer)+ResHeaderSize17);
+    ResData  =PResDataSecurity(pbyte(ResParams)+sizeof(TResParamsSecurity));
+
+    // Prepares the answer
+    Answer.Header.P=0x32;
+    Answer.Header.PDUType=PduType_userdata;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    Answer.Header.ParLen =SwapWord(sizeof(TResParamsSecurity));
+    Answer.Header.DataLen=SwapWord(0x0004);
+    // Params
+    ResParams->Head[0]=ReqParams->Head[0];
+    ResParams->Head[1]=ReqParams->Head[1];
+    ResParams->Head[2]=ReqParams->Head[2];
+    ResParams->Plen  =0x08;
+    ResParams->Uk    =0x12;
+    ResParams->Tg    =0x85; // Type response, group functions info
+    ResParams->SubFun=ReqParams->SubFun;
+    ResParams->Seq   =ReqParams->Seq;
+    ResParams->resvd =0x0000;
+    ResParams->Err   =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);
+    };
+
+    return true;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformGetClock()
+{
+    PS7ReqParams7 ReqParams;
+    PS7ResParams7 ResParams;
+    TS7Answer17 Answer;
+    PResDataGetTime Data;
+    PS7Time PTime;
+    int TotalSize;
+
+    ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
+    ResParams=PS7ResParams7(pbyte(&Answer)+ResHeaderSize17);
+    Data     =PResDataGetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
+    PTime    =PS7Time(pbyte(Data)+6);
+
+    // Prepares the answer
+    Answer.Header.P=0x32;
+    Answer.Header.PDUType=PduType_userdata;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
+    Answer.Header.DataLen=SwapWord(sizeof(TResDataGetTime));
+
+    ResParams->Head[0]=ReqParams->Head[0];
+    ResParams->Head[1]=ReqParams->Head[1];
+    ResParams->Head[2]=ReqParams->Head[2];
+    ResParams->Plen  =0x08;
+    ResParams->Uk    =0x12;
+    ResParams->Tg    =0x87; // Type response, group functions info
+    ResParams->SubFun=ReqParams->SubFun;
+    ResParams->Seq   =ReqParams->Seq;
+    ResParams->resvd =0x0000;
+    ResParams->Err   =0x0000;
+
+    Data->RetVal     =0xFF;
+    Data->TSize      =TS_ResOctet;
+    Data->Length     =SwapWord(10);
+    Data->Rsvd       =0x00;
+    Data->HiYear     =0x20; // Year 2000 +
+    FillTime(PTime);
+
+    TotalSize=36;
+    isoSendBuffer(&Answer,TotalSize);
+    DoEvent(evcClock,evrNoError,evsGetClock,0,0,0);
+    return true;
+}
+//------------------------------------------------------------------------------
+bool TS7Worker::PerformSetClock()
+{
+    PS7ReqParams7 ReqParams;
+    PS7ResParams7 ResParams;
+    PResDataSetTime Data;
+    TS7Answer17 Answer;
+    int TotalSize;
+
+    ReqParams=PS7ReqParams7(pbyte(PDUH_in)+ReqHeaderSize);
+    ResParams=PS7ResParams7(pbyte(&Answer)+ResHeaderSize17);
+    Data     =PResDataSetTime(pbyte(&Answer)+ResHeaderSize17+sizeof(TS7Params7));
+
+    // Prepares the answer
+    Answer.Header.P=0x32;
+    Answer.Header.PDUType=PduType_userdata;
+    Answer.Header.AB_EX=0x0000;
+    Answer.Header.Sequence=PDUH_in->Sequence;
+    Answer.Header.ParLen =SwapWord(sizeof(TS7Params7));
+    Answer.Header.DataLen=SwapWord(sizeof(TResDataSetTime));
+
+    ResParams->Head[0]=ReqParams->Head[0];
+    ResParams->Head[1]=ReqParams->Head[1];
+    ResParams->Head[2]=ReqParams->Head[2];
+    ResParams->Plen  =0x08;
+    ResParams->Uk    =0x12;
+    ResParams->Tg    =0x87; // Type response, group functions info
+    ResParams->SubFun=ReqParams->SubFun;
+    ResParams->Seq   =ReqParams->Seq;
+    ResParams->resvd =0x0000;
+    ResParams->Err   =0x0000;
+
+    Data->RetVal     =0x0A;
+    Data->TSize      =0x00;
+    Data->Length     =0x0000;
+
+    TotalSize=26;
+    isoSendBuffer(&Answer,TotalSize);
+    DoEvent(evcClock,evrNoError,evsSetClock,0,0,0);
+    return true;
+}
+//------------------------------------------------------------------------------
+// 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;
+    return Result;
+}
+//------------------------------------------------------------------------------
+PS7Area TSnap7Server::FindDB(word DBNumber)
+{
+    int c;
+    int max=DBLimit+1;
+
+    for (c=0; c<max; c++)
+    {
+        if (DB[c]!=NULL)
+        {
+	        if (DB[c]->Number==DBNumber)
+	        {
+                return DB[c];
+	        }
+	    }
+    }
+    return NULL;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::IndexOfDB(word DBNumber)
+{
+    int c;
+    int max=DBLimit+1;
+
+    for (c=0; c<max; c++)
+    {
+		if (DB[c]!=NULL)
+		{
+			if (DB[c]->Number==DBNumber)
+			{
+				return c;
+			}
+		}
+    }
+    return -1;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::FindFirstFreeDB()
+{
+    int c;
+    for (c=0; c < MaxDB; c++)
+    {
+        if (DB[c]==NULL)
+	        return c;
+    }
+    return -1;
+}
+//------------------------------------------------------------------------------
+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;
+    return 0;
+}
+//------------------------------------------------------------------------------
+void TSnap7Server::DisposeAll()
+{
+    PS7Area TheDB;
+    int c;
+    // Unregister DBs
+    for (c = 0; c < MaxDB; c++)
+    {
+		if (DB[c]!=NULL)
+		{
+			// 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;
+		}
+    }
+    DBCount=0;
+    // Unregister other
+    for (c = srvAreaPE; c < srvAreaDB; c++)
+        UnregisterSys(c);
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::RegisterSys(int AreaCode, void *pUsrData, word Size)
+{
+    PS7Area TheArea;
+
+    if (pUsrData==0)
+        return errSrvDBNullPointer;
+
+    if ((AreaCode<srvAreaPE) || (AreaCode>srvAreaTM))
+        return errSrvUnknownArea;
+
+    if (HA[AreaCode]==0)
+    {
+	TheArea=new TS7Area;
+	TheArea->cs=new TSnapCriticalSection();
+	TheArea->PData=pbyte(pUsrData);
+	TheArea->Size=Size;
+	HA[AreaCode]=TheArea;
+	return 0;
+    }
+    else
+        return errSrvAreaAlreadyExists;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::UnregisterDB(word DBNumber)
+{
+    PS7Area TheDB;
+    int index = IndexOfDB(DBNumber);
+    if (index==-1)
+        return errSrvInvalidParams;
+
+    // Unregister should be done with the server in stop mode
+    // however we can minimize the risk...
+    TheDB=DB[index];
+    DB[index]=NULL;
+    if (TheDB->cs!=NULL)
+        delete TheDB->cs;
+    delete TheDB;
+    DBCount--;
+
+    return 0;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::UnregisterSys(int AreaCode)
+{
+    PS7Area TheArea;
+    if (HA[AreaCode]!=NULL)
+    {
+		// Unregister should be done with the server in stop mode
+		// however we can minimize the risk...
+		TheArea=HA[AreaCode];
+		HA[AreaCode]=NULL;
+		if (TheArea->cs!=NULL)
+			 delete TheArea->cs;
+		delete TheArea;
+    }
+    return 0;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::StartTo(const char *Address)
+{
+    return TCustomMsgServer::StartTo(Address, LocalPort);
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::GetParam(int ParamNumber, void *pValue)
+{
+    switch (ParamNumber)
+    {
+	case p_u16_LocalPort:
+            *Puint16_t(pValue)=LocalPort;
+	    break;
+	case p_i32_WorkInterval:
+	    *Pint32_t(pValue)=WorkInterval;
+	    break;
+	case p_i32_MaxClients:
+	    *Pint32_t(pValue)=MaxClients;
+	    break;
+	case p_i32_PDURequest:
+		*Pint32_t(pValue) = ForcePDU;
+		break;
+	default: return errSrvInvalidParamNumber;
+    }
+    return 0;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::SetParam(int ParamNumber, void *pValue)
+{
+	switch (ParamNumber)
+	{
+	case p_u16_LocalPort:
+		if (Status == SrvStopped)
+			LocalPort = *Puint16_t(pValue);
+		else
+			return errSrvCannotChangeParam;
+		break;
+	case p_i32_PDURequest:
+		if (Status == SrvStopped)
+		{
+			int PDU = *Pint32_t(pValue);
+			if (PDU == 0)
+				ForcePDU = 0; // ForcePDU=0 --> The server accepts the client's proposal
+			else
+				{
+					if ((PDU < MinPduSize) || (PDU>IsoPayload_Size))
+						return errSrvInvalidParams; // Wrong value
+					else
+						ForcePDU = PDU; // The server imposes ForcePDU as PDU size
+				}
+	    }
+		else
+            return errSrvCannotChangeParam;
+		break;
+	case p_i32_WorkInterval:
+         WorkInterval=*Pint32_t(pValue);
+	     break;
+	case p_i32_MaxClients:
+	     if (ClientsCount==0 && Status==SrvStopped)
+	         MaxClients=*Pint32_t(pValue);
+         else
+	         return errSrvCannotChangeParam;
+         break;
+	default: return errSrvInvalidParamNumber;
+    }
+    return 0;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::RegisterArea(int AreaCode, word Index, void *pUsrData, word Size)
+{
+    if (AreaCode==srvAreaDB)
+        return RegisterDB(Index, pUsrData, Size);
+    else
+        return RegisterSys(AreaCode,pUsrData, Size);
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::UnregisterArea(int AreaCode, word Index)
+{
+    if (AreaCode==srvAreaDB)
+        return UnregisterDB(Index);
+    else
+        if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
+            return UnregisterSys(AreaCode);
+	else
+        return errSrvInvalidParams;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::LockArea(int AreaCode, word DBNumber)
+{
+  int index;
+
+  if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
+  {
+      if (HA[AreaCode]!=0)
+      {
+		  HA[AreaCode]->cs->Enter();
+		  return 0;
+      }
+      else
+		  return errSrvInvalidParams;
+  }
+  else
+      if (AreaCode==srvAreaDB)
+      {
+		  index=IndexOfDB(DBNumber);
+		  if (index!=-1)
+	  {
+	      DB[index]->cs->Enter();
+	      return 0;
+	  }
+	  else
+	      return errSrvInvalidParams;
+      }
+      else
+		  return errSrvInvalidParams;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::UnlockArea(int AreaCode, word DBNumber)
+{
+  int index;
+
+  if ((AreaCode>=srvAreaPE) && (AreaCode<=srvAreaTM))
+  {
+      if (HA[AreaCode]!=0)
+      {
+		  HA[AreaCode]->cs->Leave();
+		  return 0;
+      }
+      else
+		  return errSrvInvalidParams;
+  }
+  else
+      if (AreaCode==srvAreaDB)
+      {
+		  index=IndexOfDB(DBNumber);
+		  if (index!=-1)
+	  {
+	      DB[index]->cs->Leave();
+	      return 0;
+	  }
+	  else
+	      return errSrvInvalidParams;
+      }
+      else
+		  return errSrvInvalidParams;
+}
+//------------------------------------------------------------------------------
+int TSnap7Server::SetReadEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr) 
+{
+    OnReadEvent = PCallBack;
+    FReadUsrPtr = UsrPtr;
+    return 0;
+}
+//---------------------------------------------------------------------------
+int TSnap7Server::SetRWAreaCallBack(pfn_RWAreaCallBack PCallBack, void *UsrPtr)
+{
+	OnRWArea = PCallBack;
+	FRWAreaUsrPtr = UsrPtr;
+	ResourceLess = OnRWArea != NULL;
+	return 0;
+}
+//---------------------------------------------------------------------------
+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();
+		try
+		{ 
+			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();
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+bool TSnap7Server::DoWriteArea(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();
+		try
+		{ 
+			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, OperationWrite, &Tag, pUsrData) == 0;
+		}
+		catch (...)
+		{
+			Result = false;
+		};
+		CSRWHook->Leave();
+	}
+	return Result;
+}
+
+

+ 261 - 0
src/core/s7_server.h

@@ -0,0 +1,261 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_server_h
+#define s7_server_h
+//---------------------------------------------------------------------------
+#include "snap_tcpsrvr.h"
+#include "s7_types.h"
+#include "s7_isotcp.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
+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
+
+// 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;
+
+typedef struct{
+	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
+{
+protected:
+	virtual bool IsoPerformCommand(int &Size);
+	virtual bool ExecuteSend();
+	virtual bool ExecuteRecv();
+public:
+	TIsoTcpWorker(){};
+	~TIsoTcpWorker(){};
+	// Worker execution
+	bool Execute();
+};
+//------------------------------------------------------------------------------
+// S7 WORKER CLASS
+//------------------------------------------------------------------------------
+
+// SZL frame
+typedef struct{
+    TS7Answer17         Answer;
+    PReqFunReadSZLFirst ReqParams;
+    PS7ReqSZLData       ReqData;
+    PS7ResParams7       ResParams;
+    pbyte               ResData;
+    int                 ID;
+    int                 Index;
+    bool                SZLDone;
+}TSZL;
+
+// Current Event Info
+typedef struct{
+    word EvRetCode;
+    word EvArea;
+    word EvIndex;
+    word EvStart;
+    word EvSize;
+}TEv;
+
+// Current Block info
+typedef struct{
+  PReqFunGetBlockInfo ReqParams;
+  PResFunGetBlockInfo ResParams;
+  TS7Answer17         Answer;
+  word                evError;
+  word                DataLength;
+}TCB;
+
+class TSnap7Server; // forward declaration
+
+class TS7Worker : public TIsoTcpWorker
+{
+private:
+    PS7ReqHeader PDUH_in;
+	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);
+protected:
+    int DataSizeByte(int WordLength);
+    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,
+      word Param3, word Param4);
+    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 PerformFunctionNegotiate();
+    // Control
+    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();
+public:
+    TSnap7Server *FServer;
+    int FPDULength;
+    TS7Worker();
+    ~TS7Worker(){};
+};
+
+typedef TS7Worker *PS7Worker;
+//------------------------------------------------------------------------------
+// S7 SERVER CLASS
+//------------------------------------------------------------------------------
+extern "C"
+{
+	typedef int (S7API *pfn_RWAreaCallBack)(void *usrPtr, int Sender, int Operation, PS7Tag PTag, void *pUsrData);
+}
+const int OperationRead  = 0;
+const int OperationWrite = 1;
+
+class TSnap7Server : public TCustomMsgServer
+{
+private:
+    // 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);
+protected:
+    int DBCount;
+    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);
+public:
+    int WorkInterval;
+    byte CpuStatus;
+    TSnap7Server();
+    ~TSnap7Server();
+    int StartTo(const char *Address);
+    int GetParam(int ParamNumber, void *pValue);
+    int SetParam(int ParamNumber, void *pValue);
+    int RegisterArea(int AreaCode, word Index, void *pUsrData, word Size);
+    int UnregisterArea(int AreaCode, word Index);
+    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
+

+ 788 - 0
src/core/s7_text.cpp

@@ -0,0 +1,788 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "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;
+
+	do {
+		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;
+}
+#endif
+//---------------------------------------------------------------------------
+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);
+		}
+		else
+			strcpy(Result, CNumber);
+	}
+	else
+		strcpy(Result, CNumber);
+
+	return Result;
+}
+//---------------------------------------------------------------------------
+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);
+	else
+		*Result = '\0';
+	return Result;
+}
+
+//---------------------------------------------------------------------------
+char* IpAddressToString(int IP, char* Result)
+{
+	in_addr Addr;
+	Addr.s_addr = IP;
+	strcpy(Result, inet_ntoa(Addr));
+	return Result;
+}
+//---------------------------------------------------------------------------
+#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;
+	#endif
+	case WSAEINVALIDADDRESS:  strcpy(Result," TCP : Invalid address\0");break;
+	default:
+		{
+			char CNumber[16];
+			strcpy(Result, " TCP : Other Socket error (");
+			strcat(Result, IntToString(Error, CNumber));
+			strcat(Result, ")");
+			break;
+		}
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* IsoTextOf(int Error, char* Result)
+{
+	switch (Error)
+	{
+		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;
+		default:
+		{
+			char CNumber[16];
+			strcpy(Result, " ISO : Unknown error (0x");
+			strcat(Result, NumToString(Error, 16, 8, CNumber));
+			strcat(Result, ")");
+			break;
+		}
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* CliTextOf(int Error, char* Result)
+{
+	switch (Error)
+	{
+	  case 0 :                            *Result='\0';break;
+	  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;
+	  default                           :
+	  {
+		  char CNumber[16];
+		  strcpy(Result, "CLI : Unknown error (0x");
+		  strcat(Result, NumToString(Error, 16, 8, CNumber));
+		  strcat(Result, ")");
+		  break;
+	  }
+	};
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* SrvTextOf(int Error, char* Result)
+{
+	switch (Error)
+	{
+	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;
+	default: 
+		{
+			char CNumber[16];
+			strcpy(Result, "SRV : Unknown error (0x");
+			strcat(Result, NumToString(Error, 16, 8, CNumber));
+			strcat(Result, ")");
+			break;
+		}
+	};
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ParTextOf(int Error, char* Result)
+{
+	switch(Error)
+	{
+		case 0: *Result = '\0'; break;
+		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;
+		default:
+		{
+			char CNumber[16];
+			strcpy(Result, "PAR : Unknown error (0x");
+			strcat(Result, NumToString(Error, 16, 8, CNumber));
+			strcat(Result, ")");
+			break;
+		}
+	}
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ErrCliText(int Error, char * Result, int TextLen)
+{
+	char TcpError[128];
+	char IsoError[128];
+	char CliError[256];
+	if (Error != 0)
+	{
+		switch (Error)
+		{
+			case errLibInvalidParam  : strncpy(Result,"LIB : Invalid param supplied\0",TextLen);break;
+			case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break;
+			default :
+			{
+				CliTextOf(Error & ErrS7Mask, CliError);
+				strcat(CliError, IsoTextOf(Error & ErrIsoMask, IsoError));
+				strcat(CliError, TcpTextOf(Error & ErrTcpMask, TcpError));
+				strncpy(Result, CliError, TextLen);
+			}
+		}
+	}
+	else
+		strncpy(Result, "OK\0", TextLen);
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ErrSrvText(int Error, char* Result, int TextLen)
+{
+	char TcpError[128];
+	char IsoError[128];
+	char SrvError[256];
+	if (Error != 0)
+	{
+		switch (Error)
+		{
+		case errLibInvalidParam: strncpy(Result, "LIB : Invalid param supplied\0", TextLen); break;
+		case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break;
+		default:
+		{
+			SrvTextOf(Error & ErrS7Mask, SrvError);
+			strcat(SrvError, IsoTextOf(Error & ErrIsoMask, IsoError));
+			strcat(SrvError, TcpTextOf(Error & ErrTcpMask, TcpError));
+			strncpy(Result, SrvError, TextLen);
+		}
+		}
+	}
+	else
+		strncpy(Result, "OK\0", TextLen);
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ErrParText(int Error, char* Result, int TextLen)
+{
+	char TcpError[128];
+	char IsoError[128];
+	char ParError[256];
+	if (Error != 0)
+	{
+		switch (Error)
+		{
+		case errLibInvalidParam: strncpy(Result, "LIB : Invalid param supplied\0", TextLen); break;
+		case errLibInvalidObject: strncpy(Result, "LIB : Invalid object supplied\0", TextLen); break;
+		default:
+		{
+			ParTextOf(Error & ErrS7Mask, ParError);
+			strcat(ParError, IsoTextOf(Error & ErrIsoMask, IsoError));
+			strcat(ParError, TcpTextOf(Error & ErrTcpMask, TcpError));
+			strncpy(Result, ParError, TextLen);
+		}
+		}
+	}
+	else
+		strncpy(Result, "OK\0", TextLen);
+	return Result;
+}
+//---------------------------------------------------------------------------
+//                               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, "] ");
+	}
+	else
+		strcat(Buf, " Server ");
+	strcpy(Result, Buf);
+	return Result;
+}
+//---------------------------------------------------------------------------
+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));  
+		  break;
+      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");
+		  break;
+	  default:
+		  strcat(S, "Unknown event (");
+		  strcat(S, IntToString(Event.EvtCode, Buf));
+		  strcat(S,")"); 
+		  break;
+    };
+	strcpy(Result, S);
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* PDUText(TSrvEvent &Event, char* Result)
+{
+	char S[256];
+	char Buf[128];
+	switch (Event.EvtRetCode)
+	{
+		case evrFragmentRejected:
+			strcpy(S, "Fragment of ");
+			strcat(S, IntToString(Event.EvtParam1, Buf));
+			strcat(S, " bytes rejected");
+			break;
+		case evrMalformedPDU:
+			strcpy(S, "Malformed PDU of ");
+			strcat(S, IntToString(Event.EvtParam1, Buf));
+			strcat(S, " bytes rejected");
+			break;
+		case evrSparseBytes:
+			strcpy(S, "Message of sparse ");
+			strcat(S, IntToString(Event.EvtParam1, Buf));
+			strcat(S, " bytes rejected");
+			break;
+		case evrCannotHandlePDU:
+			strcpy(S, "Cannot handle this PDU");
+			break;
+		case evrNotImplemented:
+			switch (Event.EvtParam1)
+			{
+				case grCyclicData:
+					strcpy(S, "Function group cyclic data not yet implemented");
+					break;
+				case grProgrammer:
+					strcpy(S, "Function group programmer not yet implemented");
+					break;
+			}
+			break;
+		default:
+			strcpy(S, "Unknown Return code (");
+			strcat(S, IntToString(Event.EvtRetCode, Buf));
+			strcat(S, ")");
+			break;
+	}
+	strcpy(Result, S);
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* TxtArea(TSrvEvent &Event, char* Result)
+{
+	char S[64];
+	char Buf[32];
+	switch (Event.EvtParam1)
+    {
+		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,", "); 
+			break;
+		default: 
+			strcpy(S, "Unknown area (");
+			strcat(S, IntToString(Event.EvtParam2, Buf));
+			strcat(S,")");
+			break;
+    }
+	strcpy(Result, S);
+	return Result;
+}
+//---------------------------------------------------------------------------
+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));
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* TxtDataResult(TSrvEvent &Event, char* Result)
+{
+	char N[32];
+	switch (Event.EvtRetCode)
+    {
+        case evrNoError:
+			strcpy(Result," --> OK");
+			break;
+		case evrErrException: 
+			strcpy(Result, " --> Exception error");
+			break;
+		case evrErrAreaNotFound: 
+			strcpy(Result, " --> Area not found");
+			break;
+        case evrErrOutOfRange: 
+			strcpy(Result, " --> Out of range");
+			break;
+        case evrErrOverPDU:
+			strcpy(Result, " --> Data size exceeds PDU size");
+			break;
+		case evrErrTransportSize: 
+			strcpy(Result, " --> Invalid transport size");
+			break;
+		case evrDataSizeMismatch: 
+			strcpy(Result, " --> Data size mismatch");
+			break;
+		default: 
+			strcpy(Result, " --> Unknown error code (");
+			strcat(Result, IntToString(Event.EvtRetCode, N));
+			strcat(Result,")");
+			break;
+    };
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ControlText(word Code, char* Result)
+{
+	char N[64];
+	strcpy(Result, "CPU Control request : ");
+    switch (Code)
+    {
+        case CodeControlUnknown: 
+			strcat(Result,"Unknown");
+			break;
+		case CodeControlColdStart: 
+			strcat(Result, "Cold START --> OK");
+			break;
+		case CodeControlWarmStart: 
+			strcat(Result, "Warm START --> OK");
+			break;
+		case CodeControlStop: 
+			strcat(Result, "STOP --> OK");
+			break;
+		case CodeControlCompress: 
+			strcat(Result, "Memory compress --> OK");
+			break;
+		case CodeControlCpyRamRom: 
+			strcat(Result, "Copy Ram to Rom --> OK");
+			break;
+		case CodeControlInsDel: 
+			strcat(Result, "Block Insert or Delete --> OK");
+			break;
+        default : 
+			strcat(Result, "Unknown control code (");
+			strcat(Result, IntToString(Code, N));
+			strcat(Result,")");
+    }
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ClockText(word Code, char* Result)
+{
+    if (Code==evsGetClock)
+        strcpy(Result,"System clock read requested");
+    else
+		strcpy(Result, "System clock write requested");
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* ReadSZLText(TSrvEvent &Event, char* Result)
+{
+	char S[128];
+	char N[64];
+	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");
+    else
+		strcat(S, " --> NOT AVAILABLE");
+	strcpy(Result, S);
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* UploadText(TSrvEvent &Event, char* Result)
+{
+   strcpy(Result,"Block upload requested --> NOT PERFORMED (due to invalid security level)");
+   return Result;
+}
+//---------------------------------------------------------------------------
+char* DownloadText(TSrvEvent &Event, char* Result)
+{
+	strcpy(Result, "Block download requested --> NOT PERFORMED (due to invalid security level)");
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* StrBlockType(word Code, char* Result)
+{
+	char N[64];
+	switch (Code)
+    {
+		case Block_OB: 
+			strcpy(Result, "OB");
+			break;
+		case Block_DB: 
+			strcpy(Result, "DB");
+			break;
+		case Block_SDB: 
+			strcpy(Result, "SDB");
+			break;
+		case Block_FC: 
+			strcpy(Result, "FC");
+			break;
+		case Block_SFC: 
+			strcpy(Result, "SFC");
+			break;
+		case Block_FB: 
+			strcpy(Result, "FB");
+			break;
+		case Block_SFB: 
+			strcpy(Result, "SFB");
+			break;
+		default: 
+			strcpy(Result, "[Unknown 0x");
+			strcat(Result, NumToString(Code, 16, 4, N));
+			strcat(Result,"]");
+			break;
+    };
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* BlockInfoText(TSrvEvent &Event, char* Result)
+{
+	char S[64];
+	switch (Event.EvtParam1)
+    {
+		case evsGetBlockList: 
+			strcpy(Result, "Block list requested");
+			break;
+		case evsStartListBoT:
+			strcpy(Result, "Block of type ");
+			strcat(Result, StrBlockType(Event.EvtParam2,S));
+			strcat(Result, " list requested (start sequence)");
+			break;
+		case evsListBoT: 
+			strcpy(Result, "Block of type ");
+			strcat(Result, StrBlockType(Event.EvtParam2, S));
+			strcat(Result, " list requested (next part)");
+			break;
+		case evsGetBlockInfo: 
+			strcpy(Result, "Block info requested ");
+			strcat(Result, StrBlockType(Event.EvtParam2, S));
+			strcat(Result, " ");
+			strcat(Result, IntToString(Event.EvtParam3,S));
+			break;
+    };
+    if (Event.EvtRetCode == evrNoError)
+		strcat(Result, " --> OK");
+    else
+		strcat(Result, " --> NOT AVAILABLE");
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* SecurityText(TSrvEvent &Event, char* Result)
+{
+    switch (Event.EvtParam1)
+    {
+        case evsSetPassword:
+			strcpy(Result,"Security request : Set session password --> OK");
+			break;
+		case evsClrPassword: 
+			strcpy(Result, "Security request : Clear session password --> OK");
+			break;
+		default: 
+			strcpy(Result, "Security request : Unknown Subfunction");
+			break;
+    };
+	return Result;
+}
+//---------------------------------------------------------------------------
+char* EvtSrvText(TSrvEvent &Event, char* Result, int TextLen)
+{
+	char S[256];
+	char C[128];
+
+	if (Event.EvtCode > evcSnap7Base)
+    {
+		strcpy(S, SenderText(Event, C));
+		switch (Event.EvtCode)
+        {
+            case evcPDUincoming:
+				strcat(S, "PDU incoming : ");
+				strcat(S,PDUText(Event,C));
+				break;
+			case evcDataRead: 
+				strcat(S, "Read request, ");
+				strcat(S, TxtArea(Event, C));
+				strcat(S, TxtStartSize(Event, C));
+				strcat(S, TxtDataResult(Event, C));
+				break;
+			case evcDataWrite: 
+				strcat(S, "Write request, ");
+				strcat(S, TxtArea(Event, C));
+				strcat(S, TxtStartSize(Event, C));
+				strcat(S, TxtDataResult(Event, C));
+				break;
+			case evcNegotiatePDU:
+				strcat(S, "The client requires a PDU size of ");
+				strcat(S, IntToString(Event.EvtParam1, C));
+				strcat(S," bytes");
+				break;
+			case evcControl: 
+				strcat(S, ControlText(Event.EvtParam1,C));
+				break;
+			case evcReadSZL: 
+				strcat(S, ReadSZLText(Event,C));
+				break;
+			case evcClock: 
+				strcat(S, ClockText(Event.EvtParam1,C));
+				break;
+			case evcUpload: 
+				strcat(S, UploadText(Event,C));
+				break;
+			case evcDownload: 
+				strcat(S, DownloadText(Event,C));
+				break;
+			case evcDirectory: 
+				strcat(S, BlockInfoText(Event,C));
+				break;
+			case evcSecurity: 
+				strcat(S, SecurityText(Event,C));
+				break;
+			default:
+				strcat(S, "Unknown event (");
+				strcat(S, IntToString(Event.EvtCode, C));
+				strcat(S,")");
+				break;
+        }
+    }
+    else
+        strcpy(S,TcpServerEventText(Event,C));
+
+	strncpy(Result, S, TextLen);
+	return Result;
+}
+

+ 49 - 0
src/core/s7_text.h

@@ -0,0 +1,49 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_text_h
+#define s7_text_h
+//---------------------------------------------------------------------------
+#include "s7_micro_client.h"
+#include "s7_server.h"
+#include "s7_partner.h"
+//---------------------------------------------------------------------------
+
+const int errLibInvalidParam  = -1;
+const int errLibInvalidObject = -2;
+// 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);
+
+
+#endif
+
+

+ 1066 - 0
src/core/s7_types.h

@@ -0,0 +1,1066 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef s7_types_h
+#define s7_types_h
+//------------------------------------------------------------------------------
+#include "s7_isotcp.h"
+//------------------------------------------------------------------------------
+//                               EXPORT CONSTANTS
+// Everything added in this section has to be copied into wrappers interface
+//------------------------------------------------------------------------------
+
+#ifdef OS_WINDOWS
+#define SM7API __stdcall
+#else
+#define SM7API
+#endif
+
+  // 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;
+
+const int MaxVars     = 20;
+
+const int S7WLBit     = 0x01;
+const int S7WLByte    = 0x02;
+const int S7WLChar    = 0x03;
+const int S7WLWord    = 0x04;
+const int S7WLInt     = 0x05;
+const int S7WLDWord   = 0x06;
+const int S7WLDInt    = 0x07;
+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;
+
+  // CPU status
+const byte S7CpuStatusUnknown = 0x00;
+const byte S7CpuStatusRun     = 0x08;
+const byte S7CpuStatusStop    = 0x04;
+
+const longword evcSnap7Base           = 0x00008000;
+// 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;
+const longword evcReserved_10000000   = 0x10000000;
+const longword evcReserved_20000000   = 0x20000000;
+const longword evcReserved_40000000   = 0x40000000;
+const longword evcReserved_80000000   = 0x80000000;
+// 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 Result
+const word evrNoError                 = 0;
+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;
+
+  // Async mode
+const int amPolling   = 0;
+const int amEvent     = 1;
+const int amCallBack  = 2;
+
+//------------------------------------------------------------------------------
+//                                  PARAMS LIST            
+// 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.
+//------------------------------------------------------------------------------
+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;
+
+// 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)
+const int JobComplete  = 0;
+const int JobPending   = 1;
+
+// 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
+//==============================================================================
+#pragma pack(1)
+
+// Tag Struct
+typedef struct{
+	int Area;
+	int DBNumber;
+	int Start;
+	int Size;
+	int WordLen;
+}TS7Tag, *PS7Tag;
+
+// Incoming header, it will be mapped onto IsoPDU payload
+typedef struct {
+	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
+typedef struct{
+	byte    P;        // Telegram ID, always 32
+	byte    PDUType;  // Header type 2 or 3
+	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
+	word    Error;    // Error code
+} TS7ResHeader23;
+
+typedef TS7ResHeader23* PS7ResHeader23;
+
+// Outcoming 10 bytes header , response for Request type 7
+typedef struct{
+	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
+}TS7ResHeader17;
+
+typedef TS7ResHeader17* PS7ResHeader17;
+
+// Outcoming 10 bytes header , response for Request type 8 (server control)
+typedef struct {
+	byte    P;        // Telegram ID, always 32
+	byte    PDUType;  // Header type 8
+	word    AB_EX;    // Zero
+	word    Sequence; // Message ID. This can be used to make sure a received answer
+	word    DataLen;  // Length of data which follow this header
+	word    Error;    // Error code
+} TS7ResHeader8;
+
+typedef TS7ResHeader8* PS7ResHeader8;
+
+// Outcoming answer buffer header type 2 or header type 3
+typedef struct{
+	TS7ResHeader23 Header;
+	byte   ResData [IsoPayload_Size - sizeof(TS7ResHeader23)];
+} TS7Answer23;
+
+typedef TS7Answer23* PS7Answer23;
+
+// Outcoming buffer header type 1 or header type 7
+typedef struct {
+	TS7ResHeader17 Header;
+	byte   ResData [IsoPayload_Size - sizeof(TS7ResHeader17)];
+} TS7Answer17;
+
+typedef TS7Answer17* PS7Answer17;
+
+typedef byte   TTimeBuffer[8];
+typedef byte   *PTimeBuffer[8];
+
+typedef struct{
+   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
+typedef struct {
+	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;
+
+//==============================================================================
+//                            FUNCTION NEGOTIATE
+//==============================================================================
+typedef struct {
+	byte    FunNegotiate;
+	byte    Unknown;
+	word    ParallelJobs_1;
+	word    ParallelJobs_2;
+	word    PDULength;
+}TReqFunNegotiateParams;
+
+typedef TReqFunNegotiateParams* PReqFunNegotiateParams;
+
+typedef struct {
+	byte    FunNegotiate;
+	byte    Unknown;
+	word    ParallelJobs_1;
+	word    ParallelJobs_2;
+	word    PDULength;
+}TResFunNegotiateParams;
+
+typedef TResFunNegotiateParams* PResFunNegotiateParams;
+
+//==============================================================================
+//                               FUNCTION READ
+//==============================================================================
+typedef struct {
+	byte    ItemHead[3];
+	byte    TransportSize;
+	word    Length;
+	word    DBNumber;
+	byte    Area;
+	byte    Address[3];
+}TReqFunReadItem, * PReqFunReadItem;
+
+//typedef TReqFunReadItem;
+
+typedef struct {
+	byte   FunRead;
+	byte   ItemsCount;
+	TReqFunReadItem Items[MaxVars];
+}TReqFunReadParams;
+
+typedef TReqFunReadParams* PReqFunReadParams;
+
+typedef struct {
+	byte   FunRead;
+	byte   ItemCount;
+}TResFunReadParams;
+
+typedef TResFunReadParams* PResFunReadParams;
+
+typedef struct {
+	byte    ReturnCode;
+	byte    TransportSize;
+	word    DataLength;
+	byte    Data[IsoPayload_Size - 17]; // 17 = header + params + data header - 1
+}TResFunReadItem, *PResFunReadItem;
+
+typedef PResFunReadItem TResFunReadData[MaxVars];
+
+//==============================================================================
+//                               FUNCTION WRITE
+//==============================================================================
+typedef struct {
+	byte    ItemHead[3];
+	byte    TransportSize;
+	word    Length;
+	word    DBNumber;
+	byte    Area;
+	byte    Address[3];
+}TReqFunWriteItem, * PReqFunWriteItem;
+
+typedef struct {
+	byte   FunWrite;
+	byte   ItemsCount;
+	TReqFunWriteItem Items[MaxVars];
+}TReqFunWriteParams;
+
+typedef TReqFunWriteParams* PReqFunWriteParams;
+
+typedef struct {
+	byte    ReturnCode;
+	byte    TransportSize;
+	word    DataLength;
+	byte    Data [IsoPayload_Size - 17]; // 17 = header + params + data header -1
+}TReqFunWriteDataItem, *PReqFunWriteDataItem;
+
+typedef PReqFunWriteDataItem TReqFunWriteData[MaxVars];
+
+typedef struct {
+	byte   FunWrite;
+	byte   ItemCount;
+	byte   Data[MaxVars];
+}TResFunWrite;
+
+typedef TResFunWrite* PResFunWrite;
+
+//==============================================================================
+//                                 GROUP UPLOAD
+//==============================================================================
+typedef struct {
+	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;
+
+typedef struct {
+	byte   FunSUpld;  // function start upload 0x1D
+	byte   Data_1[6];
+	byte   Upload_ID;
+	byte   Uk[3];
+	byte   LenLoad[5];
+}TResFunStartUploadParams;
+
+typedef TResFunStartUploadParams* PResFunStartUploadParams;
+
+typedef struct {
+	byte   FunUpld;  // function upload 0x1E
+	byte   Uk6[6];   // Unknown 6 bytes
+	byte   Upload_ID;
+}TReqFunUploadParams;
+
+typedef TReqFunUploadParams* PReqFunUploadParams;
+
+typedef struct {
+	byte   FunUpld; // function upload 0x1E
+	byte   EoU;     // 0 = End Of Upload, 1 = Upload in progress
+}TResFunUploadParams;
+
+typedef TResFunUploadParams* PResFunUploadParams;
+
+typedef struct {
+	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;
+
+typedef struct {
+	word    Length;// Payload length - 4
+	byte    Uk_00; // Unknown 0x00
+	byte    Uk_FB; // Unknown 0xFB
+}TResFunUploadDataHeaderNext;
+
+typedef TResFunUploadDataHeaderNext* PResFunUploadDataHeaderNext;
+
+typedef struct {
+	word    Length;// Payload length - 4
+	byte    Uk_00; // Unknown 0x00
+	byte    Uk_FB; // Unknown 0xFB
+}TResFunUploadDataHeader;
+
+typedef TResFunUploadDataHeader* PResFunUploadDataHeader;
+
+typedef struct {
+	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;
+
+typedef struct {
+	byte   FunEUpld; // function end upload 0x1F
+	byte   Uk6[6];   // Unknown 6 bytes
+	byte   Upload_ID;
+}TReqFunEndUploadParams;
+
+typedef TReqFunEndUploadParams* PReqFunEndUploadParams;
+
+typedef struct {
+	byte   FunEUpld;  // function end upload 0x1F
+}TResFunEndUploadParams;
+
+typedef TResFunEndUploadParams* PResFunEndUploadParams;
+
+//==============================================================================
+//                               GROUP DOWNLOAD
+//==============================================================================
+typedef struct {
+	byte   FunSDwnld;   // function start Download 0x1A
+	byte   Uk6[6];      // Unknown 6 bytes
+	byte   Dwnld_ID;
+	byte   Len_1;       // 0x09
+	byte   Prefix;      // 0x5F
+	byte   BlkPrfx;     // always 0x30
+	byte   BlkType;
+	byte   AsciiBlk[5]; // BlockNum in ascii
+	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;
+
+typedef struct {
+	byte   Fun;         // pduDownload or pduDownloadEnded
+	byte   Uk7[7];
+	byte   Len_1;       // 0x09
+	byte   Prefix;      // 0x5F
+	byte   BlkPrfx;     // always 0x30
+	byte   BlkType;
+	byte   AsciiBlk[5]; // BlockNum in ascii
+	byte   P;           // 0x50 ('P')
+}TReqDownloadParams;
+
+typedef TReqDownloadParams* PReqDownloadParams;
+
+typedef struct {
+	byte   FunDwnld; // 0x1B
+	byte   EoS;      // End of sequence : 0x00 - Sequence in progress : 0x01
+}TResDownloadParams;
+
+typedef TResDownloadParams* PResDownloadParams;
+
+typedef struct {
+	word    DataLen;
+	word    FB_00;   // 0x00 0xFB
+}TResDownloadDataHeader;
+
+typedef TResDownloadDataHeader* PResDownloadDataHeader;
+typedef byte   TResEndDownloadParams;
+typedef TResEndDownloadParams* PResEndDownloadParams;
+
+typedef struct {
+	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;
+}TS7CompactBlockInfo;
+
+typedef TS7CompactBlockInfo* PS7CompactBlockInfo;
+
+typedef struct {
+	byte    Uk_20[20];
+	byte    Author[8];
+	byte    Family[8];
+	byte    Header[8];
+	byte    B1; // 0x11
+	byte    B2; // 0x00
+	word    Chksum;
+	byte    Uk_12[8];
+}TS7BlockFooter;
+
+typedef TS7BlockFooter* PS7BlockFooter;
+
+//==============================================================================
+//                          FUNCTION INSERT/DELETE
+//==============================================================================
+typedef struct {
+	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    BlkType;
+	byte    AsciiBlk[5]; // BlockNum in ascii
+	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
+//==============================================================================
+typedef struct {
+	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;
+
+typedef struct {
+	byte    Fun;     // start 0x28
+	byte    Uk_7[7]; // unknown 7
+	word    Len_1;   // Length part 1 : 0x0000
+	byte    Len_2;   // Length part 2 : 0x09
+	char    Cmd [9]; // ascii 'P_PROGRAM'
+}TReqFunPlcHotStart;
+
+typedef TReqFunPlcHotStart* PReqFunPlcHotStart;
+
+typedef struct {
+	byte    Fun;     // start 0x28
+	byte    Uk_7[7]; // unknown 7
+	word    Len_1;   // Length part 1 : 0x0002
+	word    SFun;    // 'C ' 0x4320
+	byte    Len_2;   // Length part 2 : 0x09
+	char    Cmd[9];  // ascii 'P_PROGRAM'
+}TReqFunPlcColdStart;
+
+typedef TReqFunPlcColdStart* PReqFunPlcColdStart;
+
+typedef struct {
+	byte    Fun;     // pduControl 0x28
+	byte    Uk_7[7]; // unknown 7
+	word    Len_1;   // Length part 1 : 0x0002
+	word    SFun;    // 'EP' 0x4550
+	byte    Len_2;   // Length part 2 : 0x05
+	char    Cmd[5];  // ascii '_MODU'
+}TReqFunCopyRamToRom;
+
+typedef TReqFunCopyRamToRom* PReqFunCopyRamToRom;
+
+typedef struct {
+	byte    Fun;     // pduControl 0x28
+	byte    Uk_7[7]; // unknown 7
+	word    Len_1;   // Length part 1 : 0x00
+	byte    Len_2;   // Length part 2 : 0x05
+	char    Cmd[5];  // ascii '_GARB'
+}TReqFunCompress;
+
+typedef TReqFunCompress* PReqFunCompress;
+
+typedef struct {
+	byte   ResFun;
+	byte   para;
+}TResFunCtrl;
+
+typedef TResFunCtrl* PResFunCtrl;
+
+//==============================================================================
+//                            FUNCTIONS USERDATA
+//==============================================================================
+typedef struct {
+	byte    Head[3]; // Always 0x00 0x01 0x12
+	byte    Plen;    // par len 0x04 or 0x08
+	byte    Uk;      // unknown
+	byte    Tg;      // type and group  (4 bits type and 4 bits group)
+	byte    SubFun;  // subfunction
+	byte    Seq;     // sequence
+	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];
+
+typedef struct {
+	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;
+
+typedef struct {
+	byte    Ret;
+	byte    TS;
+	word    DLen;
+}TResDataSecurity;
+
+typedef TResDataSecurity* PResDataSecurity;
+
+//==============================================================================
+//                             GROUP BLOCKS SZL
+//==============================================================================
+typedef TReqFunTypedParams TReqFunReadSZLFirst;
+typedef TReqFunReadSZLFirst* PReqFunReadSZLFirst;
+
+typedef struct {
+	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
+	word    Rsvd;    // Reserved 0x0000
+	word    ErrNo;   // Error Code
+}TReqFunReadSZLNext;
+
+typedef TReqFunReadSZLNext* PReqFunReadSZLNext;
+
+typedef struct {
+	byte    Ret;  // 0xFF for request
+	byte    TS;   // 0x09 Transport size
+	word    DLen; // Data len
+	word    ID;   // SZL-ID
+	word    Index;// SZL-Index
+}TS7ReqSZLData;
+
+typedef TS7ReqSZLData* PS7ReqSZLData;
+
+typedef struct {
+	byte    Ret;
+	byte    TS;
+	word    DLen;
+	word    ID;
+	word    Index;
+	word    ListLen;
+	word    ListCount;
+	word    Data[32747];
+}TS7ResSZLDataFirst;
+
+typedef TS7ResSZLDataFirst* PS7ResSZLDataFirst;
+
+typedef struct {
+	byte    Ret;
+	byte    TS;
+	word    DLen;
+	word    Data[32751];
+}TS7ResSZLDataNext;
+
+typedef TS7ResSZLDataNext* PS7ResSZLDataNext;
+
+typedef struct {
+	byte    Ret;
+	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;
+
+typedef struct {
+	byte    RetVal;
+	byte    TSize;
+	word    Length;
+	byte    Rsvd;
+	byte    HiYear;
+	TTimeBuffer Time;
+}TResDataGetTime;
+
+typedef TResDataGetTime* PResDataGetTime;
+typedef TResDataGetTime TReqDataSetTime;
+typedef TReqDataSetTime* PReqDataSetTime;
+
+typedef struct {
+	byte    RetVal;
+	byte    TSize;
+	word    Length;
+}TResDataSetTime;
+
+typedef TResDataSetTime* PResDataSetTime;
+
+//==============================================================================
+//                            GROUP BLOCKS INFO
+//==============================================================================
+typedef TReqFunTypedParams TReqFunGetBlockInfo;
+typedef TReqFunGetBlockInfo* PReqFunGetBlockInfo;
+
+typedef byte   TReqDataFunBlocks[4];
+typedef u_char* PReqDataFunBlocks;
+
+typedef struct {
+	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
+	word    Rsvd;    // Reserved 0x0000
+	word    ErrNo;   // Error Code
+}TResFunGetBlockInfo;
+
+typedef TResFunGetBlockInfo* PResFunGetBlockInfo;
+
+typedef struct {
+	byte    Zero;   // always 0x30 -> Ascii 0
+	byte    BType;  // Block Type
+	word    BCount; // Block count
+}TResFunGetBlockItem;
+
+typedef struct {
+	byte    RetVal;
+	byte    TRSize;
+	word    Length;
+	TResFunGetBlockItem Blocks[7];
+}TDataFunListAll;
+
+typedef TDataFunListAll* PDataFunListAll;
+
+typedef struct {
+	word    BlockNum;
+	byte    Unknown;
+	byte    BlockLang;
+}TDataFunGetBotItem;
+
+typedef struct {
+	byte    RetVal;
+	byte    TSize;
+	word    DataLen;
+	TDataFunGetBotItem Items[(IsoPayload_Size - 29 ) / 4];
+}TDataFunGetBot;
+// Note : 29 is the size of headers iso, COPT, S7 header, params, data
+
+typedef TDataFunGetBot* PDataFunGetBot;
+
+typedef struct {
+	byte   RetVal;  // 0xFF
+	byte   TSize;   // Octet (0x09)
+	word   Length;  // 0x0002
+	byte   Zero;    // Ascii '0' (0x30)
+	byte   BlkType;
+}TReqDataBlockOfType;
+
+typedef TReqDataBlockOfType* PReqDataBlockOfType;
+
+typedef struct {
+	byte    RetVal;
+	byte    TSize;
+	word    DataLen;
+	byte    BlkPrfx;     // always 0x30
+	byte    BlkType;
+	byte    AsciiBlk[5]; // BlockNum in ascii
+	byte    A;           // always 0x41 ('A')
+}TReqDataBlockInfo;
+
+typedef TReqDataBlockInfo* PReqDataBlockInfo;
+
+typedef struct {
+	byte    RetVal;
+	byte    TSize;
+	word    Length;
+	byte    Cst_b;
+	byte    BlkType;
+	word    Cst_w1;
+	word    Cst_w2;
+	word    Cst_pp;
+	byte    Unknown_1;
+	byte    BlkFlags;
+	byte    BlkLang;
+	byte    SubBlkType;
+	word    BlkNumber;
+	u_int   LenLoadMem;
+	byte    BlkSec[4];
+	u_int   CodeTime_ms;
+	word    CodeTime_dy;
+	u_int   IntfTime_ms;
+	word    IntfTime_dy;
+	word    SbbLen;
+	word    AddLen;
+	word    LocDataLen;
+	word    MC7Len;
+	byte    Author[8];
+	byte    Family[8];
+	byte    Header[8];
+	byte    Version;
+	byte    Unknown_2;
+	word    BlkChksum;
+	byte    Resvd1[4];
+	byte    Resvd2[4];
+}TResDataBlockInfo;
+
+typedef TResDataBlockInfo* PResDataBlockInfo;
+
+//==============================================================================
+//                                 BSEND / BRECV
+//==============================================================================
+typedef struct {
+	int       Size;
+	longword  R_ID;
+	byte      Data[65536];
+}TPendingBuffer;
+
+typedef struct {
+	TTPKT    TPKT;
+	TCOTP_DT COTP;
+	byte     P;
+	byte     PDUType;
+}TPacketInfo;
+
+typedef struct {
+	byte    Head[3];// Always 0x00 0x01 0x12
+	byte    Plen;   // par len 0x04 or 0x08
+	byte    Uk;     // unknown  (0x12)
+	byte    Tg;     // type and group, 4 bits type and 4 bits group  (0x46)
+	byte    SubFun; // subfunction (0x01)
+	byte    Seq;    // sequence
+	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
+
+typedef struct {
+	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;
+
+typedef struct {
+	byte   DHead[4]; // sequence 0x0A 0x00 0x00 0x00
+}TBSendResData;
+
+typedef TBSendResData* PBSendResData;
+
+#pragma pack()
+#endif // s7_types_h

+ 129 - 0
src/lib/snap7.def

@@ -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

+ 1196 - 0
src/lib/snap7_libmain.cpp

@@ -0,0 +1,1196 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.4.1 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "snap7_libmain.h"
+
+#ifndef OS_WINDOWS
+void libinit(void) __attribute__((constructor));
+void libdone(void) __attribute__((destructor));
+#endif
+
+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
+}
+
+#ifdef OS_WINDOWS
+BOOL APIENTRY DllMain (HINSTANCE hInst,
+                       DWORD reason,
+                       LPVOID reserved)
+{
+    switch (reason)
+    {
+      case DLL_PROCESS_ATTACH:
+        libinit();
+        break;
+      case DLL_PROCESS_DETACH:
+        libdone();
+        break;
+      case DLL_THREAD_ATTACH:
+        break;
+      case DLL_THREAD_DETACH:
+        break;
+    }
+    return libresult;
+}
+#endif
+
+//***************************************************************************
+// CLIENT
+//***************************************************************************
+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)
+{
+    if (Client)
+    {
+        PSnap7Client(Client)->SetConnectionParams(Address, LocalTSAP, RemoteTSAP);
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetConnectionType(S7Object Client, word ConnectionType)
+{
+    if (Client)
+    {
+        PSnap7Client(Client)->SetConnectionType(ConnectionType);
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ConnectTo(S7Object Client, const char *Address, int Rack, int Slot)
+{
+    if (Client)
+        return PSnap7Client(Client)->ConnectTo(Address, Rack, Slot);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Connect(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->Connect();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Disconnect(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->Disconnect();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetParam(S7Object Client, int ParamNumber, void *pValue)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetParam(S7Object Client, int ParamNumber, void *pValue)
+{
+    if (Client)
+        return PSnap7Client(Client)->SetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetAsCallback(S7Object Client, pfn_CliCompletion pCompletion, void *usrPtr)
+{
+    if (Client)
+        return PSnap7Client(Client)->SetAsCallback(pCompletion, usrPtr);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->ReadArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_WriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->WriteArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ReadMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->ReadMultiVars(Item, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_WriteMultiVars(S7Object Client, PS7DataItem Item, int ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->WriteMultiVars(Item, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_DBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->DBRead(DBNumber, Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_DBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->DBWrite(DBNumber, Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_MBRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->MBRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_MBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->MBWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_EBRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->EBRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_EBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->EBWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ABRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->ABRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ABWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->ABWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_TMRead(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->TMRead(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_TMWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->TMWrite(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_CTRead(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+         return PSnap7Client(Client)->CTRead(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_CTWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->CTWrite(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ListBlocks(S7Object Client, TS7BlocksList *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->ListBlocks(pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetAgBlockInfo(S7Object Client, int BlockType, int BlockNum, TS7BlockInfo *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetAgBlockInfo(BlockType, BlockNum, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetPgBlockInfo(S7Object Client, void *pBlock, TS7BlockInfo *pUsrData, int Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetPgBlockInfo(pBlock, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->ListBlocksOfType(BlockType, pUsrData, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Upload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->Upload(BlockType, BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_FullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->FullUpload(BlockType, BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Download(S7Object Client, int BlockNum, void *pUsrData, int Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->Download(BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Delete(S7Object Client, int BlockType, int BlockNum)
+{
+    if (Client)
+        return PSnap7Client(Client)->Delete(BlockType, BlockNum);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_DBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->DBGet(DBNumber, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_DBFill(S7Object Client, int DBNumber, int FillChar)
+{
+    if (Client)
+        return PSnap7Client(Client)->DBFill(DBNumber, FillChar);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetPlcDateTime(S7Object Client, tm &DateTime)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetPlcDateTime(DateTime);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetPlcDateTime(S7Object Client, tm *DateTime)
+{
+    if (Client)
+        return PSnap7Client(Client)->SetPlcDateTime(DateTime);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetPlcSystemDateTime(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->SetPlcSystemDateTime();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetOrderCode(S7Object Client, TS7OrderCode *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetOrderCode(pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetCpuInfo(S7Object Client, TS7CpuInfo *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetCpuInfo(pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetCpInfo(S7Object Client, TS7CpInfo *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetCpInfo(pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->ReadSZL(ID, Index, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->ReadSZLList(pUsrData, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_PlcHotStart(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->PlcHotStart();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_PlcColdStart(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->PlcColdStart();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_PlcStop(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->PlcStop();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_CopyRamToRom(S7Object Client, int Timeout)
+{
+    if (Client)
+        return PSnap7Client(Client)->CopyRamToRom(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_Compress(S7Object Client, int Timeout)
+{
+    if (Client)
+        return PSnap7Client(Client)->Compress(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetPlcStatus(S7Object Client, int &Status)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetPlcStatus(Status);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetProtection(S7Object Client, TS7Protection *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->GetProtection(pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_SetSessionPassword(S7Object Client, char *Password)
+{
+    if (Client)
+        return PSnap7Client(Client)->SetSessionPassword(Password);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ClearSessionPassword(S7Object Client)
+{
+    if (Client)
+        return PSnap7Client(Client)->ClearSessionPassword();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->isoExchangeBuffer(pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetExecTime(S7Object Client, int &Time)
+{
+    if (Client)
+    {
+        Time=PSnap7Client(Client)->Time();
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetLastError(S7Object Client, int &LastError)
+{
+    if (Client)
+    {
+        LastError=PSnap7Client(Client)->LastError;
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetPduLength(S7Object Client, int &Requested, int &Negotiated)
+{
+    if (Client)
+    {
+        Negotiated=PSnap7Client(Client)->PDULength;
+        Requested =PSnap7Client(Client)->PDURequest;
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_ErrorText(int Error, char *Text, int TextLen)
+{
+	try{
+		ErrCliText(Error, Text, TextLen);
+		Text[TextLen-1] = '\0';
+	}
+	catch (...){
+		return errLibInvalidParam;
+	}
+	return 0;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_GetConnected(S7Object Client, int &Connected)
+{
+    Connected=0;
+	if (Client)
+    {
+		Connected=PSnap7Client(Client)->Connected;
+		return 0;
+	}
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsReadArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsReadArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+    else
+        return errLibInvalidParam;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsWriteArea(S7Object Client, int Area, int DBNumber, int Start, int Amount, int WordLen, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsWriteArea(Area, DBNumber, Start, Amount, WordLen, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsDBRead(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsDBRead(DBNumber, Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsDBWrite(S7Object Client, int DBNumber, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsDBWrite(DBNumber, Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsMBRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsMBRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsMBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsMBWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsEBRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsEBRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsEBWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsEBWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsABRead(S7Object Client, int Start, int Size, void *pUsrData)
+{
+	if (Client)
+        return PSnap7Client(Client)->AsABRead(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsABWrite(S7Object Client, int Start, int Size, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsABWrite(Start, Size, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsTMRead(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsTMRead(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsTMWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsTMWrite(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsCTRead(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsCTRead(Start, Amount, pUsrData);
+	else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsCTWrite(S7Object Client, int Start, int Amount, void *pUsrData)
+{
+	if (Client)
+        return PSnap7Client(Client)->AsCTWrite(Start, Amount, pUsrData);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsListBlocksOfType(S7Object Client, int BlockType, TS7BlocksOfType *pUsrData, int &ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsListBlocksOfType(BlockType, pUsrData, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsReadSZL(S7Object Client, int ID, int Index, TS7SZL *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsReadSZL(ID, Index, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsReadSZLList(S7Object Client, TS7SZLList *pUsrData, int &ItemsCount)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsReadSZLList(pUsrData, ItemsCount);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsUpload(BlockType, BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsFullUpload(S7Object Client, int BlockType, int BlockNum, void *pUsrData, int &Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsFullUpload(BlockType, BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsDownload(S7Object Client, int BlockNum, void *pUsrData, int Size)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsDownload(BlockNum, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsCopyRamToRom(S7Object Client, int Timeout)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsCopyRamToRom(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsCompress(S7Object Client, int Timeout)
+{
+    if (Client)
+        return PSnap7Client(Client)->Compress(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsDBGet(S7Object Client, int DBNumber, void *pUsrData, int &Size)
+{
+	if (Client)
+        return PSnap7Client(Client)->AsDBGet(DBNumber, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_AsDBFill(S7Object Client, int DBNumber, int FillChar)
+{
+    if (Client)
+        return PSnap7Client(Client)->AsDBFill(DBNumber, FillChar);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_CheckAsCompletion(S7Object Client, int &opResult)
+{
+    if (Client)
+    {
+        if (PSnap7Client(Client)->CheckAsCompletion(opResult))
+            return JobComplete;
+        else
+            return JobPending;
+    }
+    else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Cli_WaitAsCompletion(S7Object Client, int Timeout)
+{
+    if (Client)
+        return PSnap7Client(Client)->WaitAsCompletion(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//***************************************************************************
+// SERVER
+//***************************************************************************
+S7Object S7API Srv_Create()
+{
+    return S7Object(new TSnap7Server());
+}
+//---------------------------------------------------------------------------
+void S7API Srv_Destroy(S7Object &Server)
+{
+    if (Server)
+    {
+        delete PSnap7Server(Server);
+        Server=0;
+    }
+}
+//---------------------------------------------------------------------------
+int S7API Srv_GetParam(S7Object Server, int ParamNumber, void *pValue)
+{
+    if (Server)
+        return PSnap7Server(Server)->GetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetParam(S7Object Server, int ParamNumber, void *pValue)
+{
+    if (Server)
+        return PSnap7Server(Server)->SetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_StartTo(S7Object Server, const char *Address)
+{
+    if (Server)
+        return PSnap7Server(Server)->StartTo(Address);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_Start(S7Object Server)
+{
+    if (Server)
+        return PSnap7Server(Server)->Start();
+    else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_Stop(S7Object Server)
+{
+    if (Server)
+    {
+        PSnap7Server(Server)->Stop();
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_RegisterArea(S7Object Server, int AreaCode, word Index, void *pUsrData, int Size)
+{
+    if (Server)
+        return PSnap7Server(Server)->RegisterArea(AreaCode, Index, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_UnregisterArea(S7Object Server, int AreaCode, word Index)
+{
+    if (Server)
+        return PSnap7Server(Server)->UnregisterArea(AreaCode, Index);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_LockArea(S7Object Server, int AreaCode, word Index)
+{
+    if (Server)
+        return PSnap7Server(Server)->LockArea(AreaCode, Index);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_UnlockArea(S7Object Server, int AreaCode, word Index)
+{
+    if (Server)
+        return PSnap7Server(Server)->UnlockArea(AreaCode, Index);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_GetStatus(S7Object Server, int &ServerStatus, int &CpuStatus, int &ClientsCount)
+{
+    if (Server)
+    {
+        ServerStatus=PSnap7Server(Server)->Status;
+        CpuStatus=PSnap7Server(Server)->CpuStatus;
+        ClientsCount=PSnap7Server(Server)->ClientsCount;
+        return 0;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetCpuStatus(S7Object Server, int CpuStatus)
+{
+	if (Server)
+	{
+		PSnap7Server(Server)->CpuStatus=CpuStatus;
+		return 0;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_ErrorText(int Error, char *Text, int TextLen)
+{
+	try{
+		ErrSrvText(Error, Text, TextLen);
+		Text[TextLen-1] = '\0';
+	}
+	catch (...){
+		return errLibInvalidParam;
+	}
+	return 0;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_EventText(TSrvEvent &Event, char *Text, int TextLen)
+{
+	try{
+		EvtSrvText(Event, Text, TextLen);
+		//Text[TextLen] = '\0';
+	}
+	catch (...){
+		return errLibInvalidParam;
+	}
+	return 0;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_PickEvent(S7Object Server, TSrvEvent *pEvent, int &EvtReady)
+{
+	EvtReady=0;
+	if (Server)
+	{
+		EvtReady=int(PSnap7Server(Server)->PickEvent(pEvent));
+		return 0;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_ClearEvents(S7Object Server)
+{
+	if (Server)
+	{
+		PSnap7Server(Server)->EventsFlush();
+		return 0;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_GetMask(S7Object Server, int MaskKind, longword &Mask)
+{
+	if (Server)
+	{
+		Mask=0;
+		if ((MaskKind==mkEvent) || (MaskKind==mkLog))
+		{
+			if (MaskKind==mkEvent)
+			   Mask=PSnap7Server(Server)->EventMask;
+			else
+			   Mask=PSnap7Server(Server)->LogMask;
+			return 0;
+		}
+		else
+			return errLibInvalidParam;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetMask(S7Object Server, int MaskKind, longword Mask)
+{
+	if (Server)
+	{
+		if ((MaskKind==mkEvent) || (MaskKind==mkLog))
+		{
+			if (MaskKind==mkEvent)
+			   PSnap7Server(Server)->EventMask=Mask;
+			else
+			   PSnap7Server(Server)->LogMask=Mask;
+			return 0;
+		}
+		else
+			return errLibInvalidParam;
+	}
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr)
+{
+	if (Server)
+		return PSnap7Server(Server)->SetEventsCallBack(pCallback, usrPtr);
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetReadEventsCallback(S7Object Server, pfn_SrvCallBack pCallback, void *usrPtr)
+{
+	if (Server)
+		return PSnap7Server(Server)->SetReadEventsCallBack(pCallback, usrPtr);
+	else
+		return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Srv_SetRWAreaCallback(S7Object Server, pfn_RWAreaCallBack pCallback, void *usrPtr)
+{
+	if (Server)
+		return PSnap7Server(Server)->SetRWAreaCallBack(pCallback, usrPtr);
+	else
+		return errLibInvalidObject;
+}
+//***************************************************************************
+// PARTNER
+//***************************************************************************
+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)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->GetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_SetParam(S7Object Partner, int ParamNumber, void *pValue)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->SetParam(ParamNumber, pValue);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_StartTo(S7Object Partner, const char *LocalAddress, const char *RemoteAddress,
+    word LocTsap, word RemTsap)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->StartTo(LocalAddress, RemoteAddress, LocTsap, RemTsap);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_Start(S7Object Partner)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->Start();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_Stop(S7Object Partner)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->Stop();
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_BSend(S7Object Partner, longword R_ID, void *pUsrData, int Size)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->BSend(R_ID, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_AsBSend(S7Object Partner, longword R_ID, void *pUsrData, int Size)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->AsBSend(R_ID, pUsrData, Size);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_CheckAsBSendCompletion(S7Object Partner, int &opResult)
+{
+	if (Partner)
+    {
+        if (PSnap7Partner(Partner)->CheckAsBSendCompletion(opResult))
+            return JobComplete;
+        else
+            return JobPending;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_WaitAsBSendCompletion(S7Object Partner, longword Timeout)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->WaitAsBSendCompletion(Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_SetSendCallback(S7Object Partner, pfn_ParBSendCompletion pCompletion, void *usrPtr)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->SetSendCallback(pCompletion, usrPtr);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_BRecv(S7Object Partner, longword &R_ID, void *pData, int &Size, longword Timeout)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->BRecv(R_ID, pData, Size, Timeout);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_CheckAsBRecvCompletion(S7Object Partner, int &opResult, longword &R_ID,
+	void *pData, int &Size)
+{
+    if (Partner)
+    {
+        if (PSnap7Partner(Partner)->CheckAsBRecvCompletion(opResult, R_ID, pData, Size))
+            return JobComplete;
+        else
+            return JobPending;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParBRecvCallBack pCompletion, void *usrPtr)
+{
+    if (Partner)
+        return PSnap7Partner(Partner)->SetRecvCallback(pCompletion, usrPtr);
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_GetTimes(S7Object Partner, longword &SendTime, longword &RecvTime)
+{
+    if (Partner)
+    {
+        SendTime=PSnap7Partner(Partner)->SendTime;
+        RecvTime=PSnap7Partner(Partner)->RecvTime;
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+
+}
+//---------------------------------------------------------------------------
+int S7API Par_GetStats(S7Object Partner, longword &BytesSent, longword &BytesRecv,
+    longword &SendErrors, longword &RecvErrors)
+{
+    if (Partner)
+    {
+        BytesSent=PSnap7Partner(Partner)->BytesSent;
+        BytesRecv=PSnap7Partner(Partner)->BytesRecv;
+        SendErrors=PSnap7Partner(Partner)->SendErrors;
+        RecvErrors=PSnap7Partner(Partner)->RecvErrors;
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_GetLastError(S7Object Partner, int &LastError)
+{
+    if (Partner)
+    {
+        LastError=PSnap7Partner(Partner)->LastError;
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_GetStatus(S7Object Partner, int &Status)
+{
+    if (Partner)
+    {
+        Status=PSnap7Partner(Partner)->Status();
+        return 0;
+    }
+    else
+        return errLibInvalidObject;
+}
+//---------------------------------------------------------------------------
+int S7API Par_ErrorText(int Error, char *Text, int TextLen)
+{
+	try{
+		ErrParText(Error, Text, TextLen);
+		Text[TextLen - 1] = '\0';
+	}
+	catch (...){
+		return errLibInvalidParam;
+	}
+	return 0;
+}
+

+ 201 - 0
src/lib/snap7_libmain.h

@@ -0,0 +1,201 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap7_libmain_h
+#define snap7_libmain_h
+//---------------------------------------------------------------------------
+#include "s7_client.h"
+#include "s7_server.h"
+#include "s7_partner.h"
+#include "s7_text.h"
+//---------------------------------------------------------------------------
+
+const int mkEvent  = 0;
+const int mkLog    = 1;
+
+typedef uintptr_t S7Object; // multi platform/processor object reference
+
+//==============================================================================
+// 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);
+// Data I/O functions
+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);
+// Data I/O Lean functions
+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);
+// Directory functions
+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);
+// Blocks functions
+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);
+// Date/Time functions
+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);
+// System Info functions
+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);
+// Control functions
+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);
+// Security functions
+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);
+// Low level
+EXPORTSPEC int S7API Cli_IsoExchangeBuffer(S7Object Client, void *pUsrData, int &Size);
+// Misc
+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);
+// Misc
+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, 
+	word LocTsap, word RemTsap);
+EXPORTSPEC int S7API Par_Stop(S7Object Partner);
+// BSend
+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);
+// BRecv
+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,
+    void *pData, int &Size);
+EXPORTSPEC int S7API Par_SetRecvCallback(S7Object Partner, pfn_ParBRecvCallBack pCompletion, void *usrPtr);
+// Stat
+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

+ 923 - 0
src/sys/snap_msgsock.cpp

@@ -0,0 +1,923 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "snap_msgsock.h"
+
+//---------------------------------------------------------------------------
+
+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);
+	else
+	    return Value;
+}
+//---------------------------------------------------------------------------
+longword TSnapBase::SwapDWord(longword Value)
+{
+	if (LittleEndian)
+		return (Value >> 24) | ((Value << 8) & 0x00FF0000) | ((Value >> 8) & 0x0000FF00) | (Value << 24);
+	else
+		return Value;
+}
+//---------------------------------------------------------------------------
+void Msg_CloseSocket(socket_t FSocket)
+{
+    #ifdef OS_WINDOWS
+    closesocket(FSocket);
+    #else
+    close(FSocket);
+    #endif
+}
+//---------------------------------------------------------------------------
+longword Msg_GetSockAddr(socket_t FSocket)
+{
+    sockaddr_in RemoteSin;
+    #ifdef OS_WINDOWS
+    int namelen = sizeof(RemoteSin);
+    #else
+    uint32_t namelen = sizeof(RemoteSin);
+    #endif
+    namelen=sizeof(sockaddr_in);
+    if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
+        return RemoteSin.sin_addr.s_addr;
+    else
+        return 0;
+}
+//---------------------------------------------------------------------------
+TMsgSocket::TMsgSocket()
+{
+    Pinger = new TPinger();
+    // Set Defaults
+    strcpy(LocalAddress,"0.0.0.0");
+    LocalPort=0;
+    strcpy(RemoteAddress,"127.0.0.1");
+    RemotePort=0;
+    WorkInterval=100;
+    RecvTimeout=500;
+    SendTimeout=10;
+    PingTimeout=750;
+    Connected=false;
+    FSocket=INVALID_SOCKET;
+    LastTcpError=0;
+    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));
+    LastTcpError=0;
+
+    if (in_addr!=INADDR_NONE)
+    {
+		sin.sin_addr.s_addr = in_addr;
+		sin.sin_family = AF_INET;
+		sin.sin_port = htons(Port);
+    }
+    else
+        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()
+{
+    #ifdef OS_WINDOWS
+		int namelen = sizeof(LocalSin);
+    #else
+        uint32_t namelen = sizeof(LocalSin);
+    #endif
+    if (getsockname(FSocket, (struct sockaddr*)&LocalSin, &namelen)==0)
+        GetSin(LocalSin, LocalAddress, LocalPort);
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::GetRemote()
+{
+    #ifdef OS_WINDOWS
+        int namelen = sizeof(RemoteSin);
+    #else
+        uint32_t namelen = sizeof(RemoteSin);
+    #endif
+	if (getpeername(FSocket,(struct sockaddr*)&RemoteSin, &namelen)==0)
+		GetSin(RemoteSin, RemoteAddress, RemotePort);
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::GetLastSocketError()
+{
+#ifdef OS_WINDOWS
+    return WSAGetLastError();
+#else    
+    return errno;
+#endif
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::Purge()
+{
+    // small buffer to empty the socket
+    char Trash[512];
+    int Read;
+    if (LastTcpError!=WSAECONNRESET)
+    {
+        if (CanRead(0)) {
+           do
+           {
+               Read=recv(FSocket, Trash, 512, MSG_NOSIGNAL );
+           } while(Read==512);
+        }
+    }
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::CreateSocket()
+{
+	DestroySocket();
+	LastTcpError=0;
+	FSocket =socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
+	if (FSocket!=INVALID_SOCKET)
+		SetSocketOptions();
+	else
+		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;
+    if (FSocket!=INVALID_SOCKET)
+    {
+		SetSocketOptions();
+		GetLocal();
+		GetRemote();
+		GotSocket();
+    }
+    Connected=FSocket!=INVALID_SOCKET;
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::DestroySocket()
+{
+    if(FSocket != INVALID_SOCKET)
+    {        
+        if (shutdown(FSocket, SD_SEND)==0)
+			Purge();
+	#ifdef OS_WINDOWS
+		closesocket(FSocket);
+	#else
+		close(FSocket);
+	#endif
+		FSocket=INVALID_SOCKET;
+    }
+    LastTcpError=0;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::WaitingData()
+{
+    int result = 0;
+    u_long x = 0;
+#ifdef OS_WINDOWS
+    if (ioctlsocket(FSocket, FIONREAD, &x) == 0)
+        result = x;
+#else
+    if (ioctl(FSocket, FIONREAD, &x) == 0)
+        result = x;
+#endif
+    if (result>MaxPacketSize)
+        result = MaxPacketSize;
+    return result;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::WaitForData(int Size, int Timeout)
+{
+    longword Elapsed;
+
+    // Check for connection active
+    if (CanRead(0) && (WaitingData()==0))
+        LastTcpError=WSAECONNRESET;
+    else
+        LastTcpError=0;
+
+    // Enter main loop
+    if (LastTcpError==0)
+    {
+        Elapsed =SysGetTick();
+        while((WaitingData()<Size) && (LastTcpError==0))
+        {
+            // Checks timeout
+            if (DeltaTime(Elapsed)>=(longword)(Timeout))
+                LastTcpError =WSAETIMEDOUT;
+            else
+                SysSleep(1);
+        }
+    }
+    if(LastTcpError==WSAECONNRESET)
+            Connected =false;
+
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::SetSocketOptions()
+{
+    int NoDelay = 1;
+	int KeepAlive = 1;
+    LastTcpError=0;
+    SockCheck(setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&NoDelay, sizeof(NoDelay)));
+
+	if (LastTcpError==0)
+        SockCheck(setsockopt(FSocket, SOL_SOCKET, SO_KEEPALIVE,(char*)&KeepAlive, sizeof(KeepAlive)));
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::SockCheck(int SockResult)
+{
+  if (SockResult == (int)(SOCKET_ERROR))
+     LastTcpError = GetLastSocketError();
+
+  return LastTcpError;
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::CanWrite(int Timeout)
+{
+    timeval TimeV;
+    int64_t x;
+    fd_set FDset;
+
+	if(FSocket == INVALID_SOCKET)
+		return false;
+
+    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) 
+    {
+        LastTcpError = GetLastSocketError();
+        x=0;
+    }
+    return (x > 0);
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::CanRead(int Timeout)
+{
+    timeval TimeV;
+    int64_t x;
+    fd_set FDset;
+
+	if(FSocket == INVALID_SOCKET)
+		return false;
+
+	TimeV.tv_usec = (Timeout % 1000) * 1000;
+    TimeV.tv_sec = Timeout / 1000;
+
+    FD_ZERO(&FDset);
+    FD_SET(FSocket, &FDset);
+
+    x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
+    if (x==(int)SOCKET_ERROR)
+    {
+       LastTcpError = GetLastSocketError();
+       x=0;
+    }
+    return (x > 0);
+}
+//---------------------------------------------------------------------------
+#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();
+		if (LastTcpError == 0) {
+			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) {
+							LastTcpError = GetLastSocketError();
+						}
+						else {
+							// 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;
+							}
+							else {
+								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;
+										}
+										else {
+											if (fcntl(FSocket, F_SETFL, flags) != -1) {
+												GetLocal();
+												ClientHandle = LocalSin.sin_addr.s_addr;
+											}
+											else {
+												LastTcpError = GetLastSocketError();
+											}
+										}
+									}
+									else {
+										LastTcpError = GetLastSocketError();
+									}
+								}
+								else {
+									LastTcpError = -1;
+								}
+							}
+						} // still connecting
+					} 
+					else if (n == 0) { 
+						// connected immediatly
+						GetLocal();
+						ClientHandle = LocalSin.sin_addr.s_addr;
+					}
+				}
+				else {
+					LastTcpError = GetLastSocketError();
+				} // fcntl(F_SETFL)
+			}
+			else {
+				LastTcpError = GetLastSocketError();
+			} // fcntl(F_GETFL)
+		} //valid socket 
+	} // LastTcpError==0
+	Connected=LastTcpError==0;
+ 	return LastTcpError;
+}
+#else
+//
+// Regular connection (Windows)
+//
+int TMsgSocket::SckConnect()
+{
+    int Result;
+    SetSin(RemoteSin, RemoteAddress, RemotePort);
+    if (LastTcpError==0)
+    {
+        if (Ping(RemoteSin))
+        {
+            CreateSocket();
+            if (LastTcpError==0)
+            {
+                Result=connect(FSocket, (struct sockaddr*)&RemoteSin, sizeof(RemoteSin));
+                if (SockCheck(Result)==0)
+                {
+                    GetLocal();
+                    // Client handle is self_address (here the connection is ACTIVE)
+                    ClientHandle=LocalSin.sin_addr.s_addr;
+                }
+            }
+        }
+        else
+            LastTcpError=WSAEHOSTUNREACH;
+    }
+    Connected=LastTcpError==0;
+    return LastTcpError;
+}
+#endif
+//---------------------------------------------------------------------------
+void TMsgSocket::SckDisconnect()
+{
+    DestroySocket();
+    Connected=false;
+}
+//---------------------------------------------------------------------------
+void TMsgSocket::ForceClose()
+{
+    if(FSocket != INVALID_SOCKET)
+    {
+        try {
+            #ifdef OS_WINDOWS
+            closesocket(FSocket);
+            #else
+            close(FSocket);
+            #endif
+        } catch (...) {
+        }
+        FSocket=INVALID_SOCKET;
+    }
+    LastTcpError=0;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::SckBind()
+{
+    int Res;
+    int Opt=1;
+    SetSin(LocalSin, LocalAddress, LocalPort);
+    if (LastTcpError==0)
+    {
+        CreateSocket();
+        if (LastTcpError==0)
+        {		
+            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;
+            }
+        }
+    }
+    else
+        LastTcpError=WSAEINVALIDADDRESS;
+
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::SckListen()
+{
+    LastTcpError=0;
+    SockCheck(listen(FSocket ,SOMAXCONN));
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::Ping(char *Host)
+{
+    return Pinger->Ping(Host, PingTimeout);
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::Ping(sockaddr_in Addr)
+{
+	if (PingTimeout == 0)
+		return true;
+	else
+        return Pinger->Ping(Addr.sin_addr.s_addr, PingTimeout);
+}
+//---------------------------------------------------------------------------
+socket_t TMsgSocket::SckAccept()
+{
+    socket_t result;
+    LastTcpError=0;
+    result = accept(FSocket, NULL, NULL);
+    if(result==INVALID_SOCKET)
+        LastTcpError =GetLastSocketError();
+    return result;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::SendPacket(void *Data, int Size)
+{
+    int Result;
+    
+    LastTcpError=0;
+    if (SendTimeout>0)
+    {
+        if (!CanWrite(SendTimeout))
+        {
+            LastTcpError = WSAETIMEDOUT;
+            return LastTcpError;
+        }
+    }
+    if (send(FSocket, (char*)Data, Size, MSG_NOSIGNAL)==Size)
+        return 0;
+    else
+        Result =SOCKET_ERROR;
+
+    SockCheck(Result);
+    return Result;
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::PacketReady(int Size)
+{
+	return (WaitingData()>=Size);
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::Receive(void *Data, int BufSize, int &SizeRecvd)
+{
+    LastTcpError=0;
+    if (CanRead(RecvTimeout))
+    {
+        SizeRecvd=recv(FSocket ,(char*)Data ,BufSize ,MSG_NOSIGNAL );
+
+        if (SizeRecvd>0) // something read (default case)
+           LastTcpError=0;
+        else
+            if (SizeRecvd==0)
+                LastTcpError = WSAECONNRESET;  // Connection reset by Peer
+            else
+                LastTcpError=GetLastSocketError(); // we need to know what happened
+    }
+    else
+        LastTcpError = WSAETIMEDOUT;
+
+    if (LastTcpError==WSAECONNRESET)
+        Connected = false;
+
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::RecvPacket(void *Data, int Size)
+{
+    int BytesRead;
+    WaitForData(Size, RecvTimeout);
+    if (LastTcpError==0)
+    {
+        BytesRead=recv(FSocket, (char*)Data, Size, MSG_NOSIGNAL);
+        if (BytesRead==0)
+            LastTcpError = WSAECONNRESET;  // Connection reset by Peer
+        else
+            if (BytesRead<0)
+                LastTcpError = GetLastSocketError();
+    }
+    else // After the timeout the bytes waiting were less then we expected
+        if (LastTcpError==WSAETIMEDOUT)
+            Purge();
+
+    if (LastTcpError==WSAECONNRESET)
+        Connected =false;
+
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+int TMsgSocket::PeekPacket(void *Data, int Size)
+{
+    int BytesRead;
+    WaitForData(Size, RecvTimeout);
+    if (LastTcpError==0)
+    {
+        BytesRead=recv(FSocket, (char*)Data, Size, MSG_PEEK | MSG_NOSIGNAL );
+        if (BytesRead==0)
+            LastTcpError = WSAECONNRESET;  // Connection reset by Peer
+        else
+            if (BytesRead<0)
+                LastTcpError = GetLastSocketError();
+    }
+    else // After the timeout the bytes waiting were less then we expected
+        if (LastTcpError==WSAETIMEDOUT)
+            Purge();
+
+    if (LastTcpError==WSAECONNRESET)
+        Connected =false;
+
+    return LastTcpError;
+}
+//---------------------------------------------------------------------------
+bool TMsgSocket::Execute()
+{
+    return true;
+}
+//==============================================================================
+// PING
+//==============================================================================
+
+static int PingKind;
+
+#ifdef OS_WINDOWS
+// iphlpapi, is loaded dinamically because if this fails we can still try
+// to use raw sockets
+
+static char const *iphlpapi = "\\iphlpapi.dll";
+#pragma pack(1)
+
+//typedef byte TTxBuffer[40];
+typedef byte TTxBuffer[32];
+#pragma pack()
+
+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);
+	}
+	else
+		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);
+    }
+    else
+        return false;
+}
+
+void IcmpDone()
+{
+    if (IcmpDllHandle!=0)
+       FreeLibrary(IcmpDllHandle);
+    IcmpAvail=false;
+}
+#endif
+
+//---------------------------------------------------------------------------
+//  RAW Socket Pinger
+//---------------------------------------------------------------------------
+TRawSocketPinger::TRawSocketPinger()
+{
+    FSocket =socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+    FId  =word(size_t(this));
+    FSeq =0;
+}
+//---------------------------------------------------------------------------
+TRawSocketPinger::~TRawSocketPinger()
+{
+    if (FSocket!=INVALID_SOCKET)
+    {
+      #ifdef OS_WINDOWS
+          closesocket(FSocket);
+      #else
+          close(FSocket);
+      #endif
+      FSocket=INVALID_SOCKET;
+    };
+}
+//---------------------------------------------------------------------------
+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;
+    int c;
+    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)
+{
+    timeval TimeV;
+    int64_t x;
+    fd_set FDset;
+
+    TimeV.tv_usec = (Timeout % 1000) * 1000;
+    TimeV.tv_sec = Timeout / 1000;
+
+    FD_ZERO(&FDset);
+    FD_SET(FSocket, &FDset);
+
+    x = select(FSocket + 1, &FDset, NULL, NULL, &TimeV); //<-Ignore this warning in 64bit Visual Studio
+    if (x==(int)(SOCKET_ERROR))
+       x=0;
+    return (x > 0);
+}
+//---------------------------------------------------------------------------
+bool TRawSocketPinger::Ping(longword ip_addr, int Timeout)
+{
+    sockaddr_in LSockAddr;
+    sockaddr_in RSockAddr;
+    PIcmpReply Reply;
+
+    if (FSocket==INVALID_SOCKET)
+      return true;
+
+    // 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)
+        return false;
+    // Connect to remote (not a really TCP connection, only to setup the socket)
+    if (connect(FSocket, (struct sockaddr*)&RSockAddr, sizeof(sockaddr_in))!=0)
+        return false;
+    // Send ICMP packet
+    if (send(FSocket, (char*)SendPacket, sizeof(TIcmpPacket), MSG_NOSIGNAL)!=int(sizeof(TIcmpPacket)))
+        return false;
+    // 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)))
+        return false;
+    // 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();
+    bool Result;
+    Result=RawPinger->Ping(ip_addr, Timeout);
+    delete RawPinger;
+    return Result;
+}
+//---------------------------------------------------------------------------
+#ifdef OS_WINDOWS
+bool TPinger::WinPing(longword ip_addr, int Timeout)
+{
+    HANDLE PingHandle;
+    TTxBuffer TxBuffer;
+    TIcmpBuffer IcmpBuffer;
+    bool Result;
+
+    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);
+        return Result;
+    }
+    else
+        return false;
+}
+#endif
+//---------------------------------------------------------------------------
+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)
+{
+#ifdef OS_WINDOWS
+    if (PingKind==pkWinHelper)
+        return WinPing(ip_addr, Timeout);
+    else
+#endif
+    if (PingKind==pkRawSocket)
+        return RawPing(ip_addr, Timeout);
+    else
+        return true; // we still need to continue
+}
+//---------------------------------------------------------------------------
+// Checks if raw sockets are allowed
+//---------------------------------------------------------------------------
+bool RawSocketsCheck()
+{
+    socket_t RawSocket;
+    bool Result;
+    RawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+
+    Result=RawSocket != INVALID_SOCKET;
+    if (Result)
+    #ifdef OS_WINDOWS
+        closesocket(RawSocket);
+    #else
+        close(RawSocket);
+    #endif
+
+    return Result;
+}
+//---------------------------------------------------------------------------
+// Sockets init
+// - Winsock Startup (Windows)
+// - ICMP Helper Load (Windows)
+// - Check for raw socket (Unix or Windows if ICMP load failed)
+//---------------------------------------------------------------------------
+SocketsLayer::SocketsLayer()
+{
+#ifdef OS_WINDOWS
+    timeBeginPeriod(1); // it's not strictly related to socket but here is a nice place
+    WSAStartup(0x202,&wsaData);
+    if (IcmpInit())
+       PingKind=pkWinHelper;
+    else
+#endif
+    if (RawSocketsCheck())
+        PingKind=pkRawSocket;
+    else
+        PingKind=pkCannotPing;
+}
+
+SocketsLayer::~SocketsLayer()
+{
+#ifdef OS_WINDOWS
+    IcmpDone();
+    WSACleanup();
+    timeEndPeriod(1);
+#endif
+}
+
+

+ 339 - 0
src/sys/snap_msgsock.h

@@ -0,0 +1,339 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap_msgsock_h
+#define snap_msgsock_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+#include "snap_sysutils.h"
+//----------------------------------------------------------------------------
+#if defined(OS_WINDOWS) || defined (OS_SOLARIS) || defined(OS_OSX)
+# define MSG_NOSIGNAL    0
+#endif
+//----------------------------------------------------------------------------
+// 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
+#endif
+#ifdef NON_BLOCKING_CONNECT
+    #include <fcntl.h>
+#endif
+//----------------------------------------------------------------------------
+/*
+  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.
+*/
+
+#ifdef OS_WINDOWS
+typedef SOCKET socket_t;
+#else
+typedef int socket_t;
+#endif
+
+//----------------------------------------------------------------------------
+#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
+#if defined(PLATFORM_UNIX) || defined(OS_OSX)
+
+#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
+#endif
+
+#define WSAEINVALIDADDRESS   12001
+
+#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
+//---------------------------------------------------------------------------
+#pragma pack(1)
+typedef struct{
+    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;
+
+typedef struct{
+    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;
+
+typedef struct{
+    TIcmpHeader Header;
+    byte Data[32]; // use the well known default
+}TIcmpPacket, *PIcmpPacket;
+
+typedef struct{
+    TIPHeader IPH;
+    TIcmpPacket ICmpReply;
+}TIcmpReply, *PIcmpReply;
+#pragma pack()
+
+//---------------------------------------------------------------------------
+class TRawSocketPinger
+{
+private:
+    socket_t FSocket;
+    PIcmpPacket SendPacket;
+    TIcmpBuffer IcmpBuffer;
+    word FId, FSeq;
+    void InitPacket();
+    word PacketChecksum();
+    bool CanRead(int Timeout);
+public:
+    bool Ping(longword ip_addr, int Timeout);
+    TRawSocketPinger();
+    ~TRawSocketPinger();
+};
+typedef TRawSocketPinger *PRawSocketPinger;
+//---------------------------------------------------------------------------
+class TPinger
+{
+private:
+    PRawSocketPinger RawPinger;
+    bool RawAvail;
+#ifdef OS_WINDOWS
+	bool WinPing(longword ip_addr, int Timeout);
+#endif
+	bool RawPing(longword ip_addr, int Timeout);
+public:
+    TPinger();
+    ~TPinger();
+	bool Ping(char *Host, int Timeout);
+	bool Ping(longword ip_addr, int Timeout);
+};
+typedef TPinger *PPinger;
+//---------------------------------------------------------------------------
+class TSnapBase // base class endian-aware
+{
+private:
+		bool LittleEndian;
+protected:
+		longword SwapDWord(longword Value);
+		word SwapWord(word Value);
+public:
+		TSnapBase();
+};
+//---------------------------------------------------------------------------
+class TMsgSocket : public TSnapBase
+{
+private:
+        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);
+protected:
+        socket_t FSocket;
+        sockaddr_in LocalSin;
+        sockaddr_in RemoteSin;
+        //--------------------------------------------------------------------------
+        // 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();
+public:
+        longword ClientHandle;
+        longword LocalBind;
+        // Coordinates Address:Port
+        char LocalAddress[16];
+        char RemoteAddress[16];
+        word LocalPort;
+        word RemotePort;
+        // "speed" of the socket listener (used server-side)
+        int WorkInterval;
+        // 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..
+        bool CanRead(int Timeout);
+        // 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
+{
+private:
+#ifdef OS_WINDOWS
+    WSADATA wsaData;
+#endif
+public:
+    SocketsLayer();
+    ~SocketsLayer();
+};
+
+#endif // snap_msgsock_h

+ 152 - 0
src/sys/snap_platform.h

@@ -0,0 +1,152 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.4.1 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap_platform_h
+#define snap_platform_h
+//---------------------------------------------------------------------------
+#if defined (_WIN32)|| defined(_WIN64)|| defined(__WIN32__) || defined(__WINDOWS__)
+# define OS_WINDOWS
+#endif
+
+// Visual Studio needs this to use the correct time_t size
+#if defined (_WIN32) && !defined(_WIN64) && !defined(_EMBEDDING_VS2013UP)
+  # define _USE_32BIT_TIME_T 
+#endif
+
+// Linux, BSD and Solaris define "unix", OSX doesn't, even though it derives from BSD
+#if defined(unix) || defined(__unix__) || defined(__unix)
+# define PLATFORM_UNIX
+#endif
+
+#if BSD>=0
+# define OS_BSD
+#endif
+
+#if __APPLE__
+# define OS_OSX
+#endif
+
+#if defined(__SVR4) || defined(__svr4__)
+# define OS_SOLARIS
+// 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
+#endif
+
+#if defined(PLATFORM_UNIX)
+# include <unistd.h>
+# include <sys/param.h>
+# if defined(_POSIX_VERSION)
+#   define POSIX
+# endif
+#endif
+
+#ifdef OS_OSX
+# include <unistd.h>
+#endif
+
+#if (!defined (OS_WINDOWS)) && (!defined(PLATFORM_UNIX)) && (!defined(OS_BSD)) && (!defined(OS_OSX))
+# error platform still unsupported (please add it yourself and report ;-)
+#endif
+
+// 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
+# endif
+#else
+# include <stdint.h>
+#endif
+
+#include <time.h>
+#include <cstring>
+#include <stdlib.h>
+
+#ifdef OS_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <winsock2.h>
+# include <mmsystem.h>
+#endif
+
+#ifdef OS_SOLARIS
+# include <sys/filio.h>
+# include <cstdlib>
+# include <string.h>
+#endif
+
+#if defined(PLATFORM_UNIX) || defined(OS_OSX)
+# 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>
+#endif
+
+#ifdef OS_WINDOWS
+# define EXPORTSPEC extern "C" __declspec ( dllexport )
+# define S7API __stdcall
+#else
+# define EXPORTSPEC extern "C"
+# define S7API
+#endif
+
+// Exact length types regardless of platform/processor
+// 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 uint8_t    byte;
+typedef uint16_t   word;
+typedef uint32_t   longword;
+typedef byte       *pbyte;
+typedef word       *pword;
+typedef uintptr_t  snap_obj; // multi platform/processor object reference
+
+#ifndef OS_WINDOWS
+# define INFINITE  0XFFFFFFFF
+#endif
+
+
+#endif // snap_platform_h

+ 73 - 0
src/sys/snap_sysutils.cpp

@@ -0,0 +1,73 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "snap_sysutils.h"
+
+#ifdef OS_OSX
+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;
+    return 0;
+}
+#endif
+
+//---------------------------------------------------------------------------
+longword SysGetTick()
+{
+#ifdef OS_WINDOWS
+    return timeGetTime();
+#else
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return (longword) (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
+#endif
+}
+//---------------------------------------------------------------------------
+void SysSleep(longword Delay_ms)
+{
+#ifdef OS_WINDOWS
+	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);
+#endif
+}
+//---------------------------------------------------------------------------
+longword DeltaTime(longword &Elapsed)
+{
+    longword TheTime;
+    TheTime=SysGetTick();
+    // Checks for rollover
+    if (TheTime<Elapsed)
+        Elapsed=0;
+    return TheTime-Elapsed;
+}

+ 39 - 0
src/sys/snap_sysutils.h

@@ -0,0 +1,39 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap_sysutils_h
+#define snap_sysutils_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+//---------------------------------------------------------------------------
+#ifdef OS_OSX
+# define CLOCK_MONOTONIC 0
+#endif
+
+longword SysGetTick();
+void SysSleep(longword Delay_ms);
+longword DeltaTime(longword &Elapsed);
+
+#endif // snap_sysutils_h

+ 487 - 0
src/sys/snap_tcpsrvr.cpp

@@ -0,0 +1,487 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "snap_tcpsrvr.h"
+//---------------------------------------------------------------------------
+// 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;
+    pbyte PBlock;
+
+    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;
+        return true;
+    }
+    else
+        return false;
+}
+//---------------------------------------------------------------------------
+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)
+    {
+        try
+        {
+            if (!WorkerSocket->Execute()) // False -> End of Activities
+                SelfClose = true;
+        } catch (...)
+        {
+            Exception = true;
+        }
+    };
+    if (!FServer->Destroying)
+    {
+        // Exception detected during Worker activity
+        if (Exception)
+        {
+            WorkerSocket->ForceClose();
+            FServer->DoEvent(WorkerSocket->ClientHandle, evcClientException, 0, 0, 0, 0, 0);
+        }
+        else
+            if (SelfClose)
+        {
+            FServer->DoEvent(WorkerSocket->ClientHandle, evcClientDisconnected, 0, 0, 0, 0, 0);
+        }
+        else
+            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) 
+{
+    FServer = Server;
+    FListener = Listener;
+    FreeOnTerminate = false;
+}
+//---------------------------------------------------------------------------
+
+void TMsgListenerThread::Execute() 
+{
+    socket_t Sock;
+    bool Valid;
+
+    while (!Terminated)
+    {
+        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);
+            }
+            else
+                if (Valid)
+                    Msg_CloseSocket(Sock);
+        };
+    }
+}
+//---------------------------------------------------------------------------
+// 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;
+    Destroying = false;
+    FLastError = 0;
+    ClientsCount = 0;
+    LocalBind = 0;
+    MaxClients = MaxWorkers;
+    OnEvent = NULL;
+}
+//---------------------------------------------------------------------------
+TCustomMsgServer::~TCustomMsgServer() 
+{
+    Destroying = true;
+    Stop();
+    OnEvent = NULL;
+    delete CSList;
+    delete CSEvent;
+    delete FEventQueue;
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::LockList() 
+{
+    CSList->Enter();
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::UnlockList() 
+{
+    CSList->Leave();
+}
+//---------------------------------------------------------------------------
+int TCustomMsgServer::FirstFree() 
+{
+    int i;
+    for (i = 0; i < MaxWorkers; i++)
+    {
+        if (Workers[i] == 0)
+            return i;
+    }
+    return -1;
+}
+//---------------------------------------------------------------------------
+
+int TCustomMsgServer::StartListener() 
+{
+    int Result;
+    // Creates the listener
+    SockListener = new TMsgSocket();
+    strncpy(SockListener->LocalAddress, FLocalAddress, 16);
+    SockListener->LocalPort = LocalPort;
+    // Binds
+    Result = SockListener->SckBind();
+    if (Result == 0)
+    {
+        LocalBind = SockListener->LocalBind;
+        // Listen
+        Result = SockListener->SckListen();
+        if (Result == 0)
+        {
+            // Creates the Listener thread
+            ServerThread = new TMsgListenerThread(SockListener, this);
+            ServerThread->Start();
+        }
+        else
+            delete SockListener;
+    }
+    else
+        delete SockListener;
+
+    return Result;
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::TerminateAll() 
+{
+    int c;
+    longword Elapsed;
+    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)
+                SysSleep(100);
+        };
+        if (ClientsCount > 0)
+            KillAll(); // one o more threads are hanged
+        ClientsCount = 0;
+    }
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::KillAll() 
+{
+    int c, cnt = 0;
+    LockList();
+    for (c = 0; c < MaxWorkers; c++)
+    {
+        if (Workers[c] != 0)
+            try
+            {
+                PMsgWorkerThread(Workers[c])->Kill();
+                PMsgWorkerThread(Workers[c])->WorkerSocket->ForceClose();
+                delete PMsgWorkerThread(Workers[c]);
+                Workers[c] = 0;
+                cnt++;
+            } catch (...)
+            {
+            };
+    }
+    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) 
+{
+    PWorkerSocket Result;
+    // Creates a funny default class : a tcp echo worker
+    Result = new TEcoTcpWorker();
+    Result->SetSocket(Sock);
+    return Result;
+}
+//---------------------------------------------------------------------------
+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))
+    {
+        CSEvent->Enter();
+
+        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))
+            try
+            { // callback is outside here, we have to shield it
+                OnEvent(FUsrPtr, &SrvEvent, sizeof (TSrvEvent));
+            } catch (...)
+            {
+            };
+
+        if (GoLog)
+            FEventQueue->Insert(&SrvEvent);
+
+        CSEvent->Leave();
+    };
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::Delete(int Index) 
+{
+    LockList();
+    Workers[Index] = 0;
+    ClientsCount--;
+    UnlockList();
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::Incoming(socket_t Sock) 
+{
+    int idx;
+    PWorkerSocket WorkerSocket;
+    longword ClientHandle = Msg_GetSockAddr(Sock);
+
+    if (CanAccept(Sock))
+    {
+        LockList();
+        // 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);
+        }
+        else
+        {
+            DoEvent(ClientHandle, evcClientNoRoom, 0, 0, 0, 0, 0);
+            Msg_CloseSocket(Sock);
+        }
+        UnlockList();
+    }
+    else
+    {
+        Msg_CloseSocket(Sock);
+        DoEvent(ClientHandle, evcClientRejected, 0, 0, 0, 0, 0);
+    };
+}
+//---------------------------------------------------------------------------
+int TCustomMsgServer::Start() 
+{
+    int Result = 0;
+    if (Status != SrvRunning)
+    {
+        Result = StartListener();
+        if (Result != 0)
+        {
+            DoEvent(0, evcListenerCannotStart, Result, 0, 0, 0, 0);
+            Status = SrvError;
+        }
+        else
+        {
+            DoEvent(0, evcServerStarted, SockListener->ClientHandle, LocalPort, 0, 0, 0);
+            Status = SrvRunning;
+        };
+    };
+    FLastError = Result;
+    return Result;
+}
+//---------------------------------------------------------------------------
+int TCustomMsgServer::StartTo(const char *Address, word Port) 
+{
+    strncpy(FLocalAddress, Address, 16);
+    LocalPort = Port;
+    return Start();
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::Stop() 
+{
+    if (Status == SrvRunning)
+    {
+        // Kills the listener thread
+        ServerThread->Terminate();
+        if (ServerThread->WaitFor(ThTimeout) != WAIT_OBJECT_0)
+            ServerThread->Kill();
+        delete ServerThread;
+        // Kills the listener
+        delete SockListener;
+
+        // Terminate all client threads
+        TerminateAll();
+
+        Status = SrvStopped;
+        LocalBind = 0;
+        DoEvent(0, evcServerStopped, 0, 0, 0, 0, 0);
+    };
+    FLastError = 0;
+}
+//---------------------------------------------------------------------------
+int TCustomMsgServer::SetEventsCallBack(pfn_SrvCallBack PCallBack, void *UsrPtr) 
+{
+    OnEvent = PCallBack;
+    FUsrPtr = UsrPtr;
+    return 0;
+}
+//---------------------------------------------------------------------------
+bool TCustomMsgServer::PickEvent(void *pEvent) 
+{
+    try
+    {
+        return FEventQueue->Extract(pEvent);
+    } catch (...)
+    {
+        return false;
+    };
+}
+//---------------------------------------------------------------------------
+bool TCustomMsgServer::EventEmpty() 
+{
+    return FEventQueue->Empty();
+}
+//---------------------------------------------------------------------------
+void TCustomMsgServer::EventsFlush() 
+{
+    CSEvent->Enter();
+    FEventQueue->Flush();
+    CSEvent->Leave();
+}
+//---------------------------------------------------------------------------
+
+

+ 247 - 0
src/sys/snap_tcpsrvr.h

@@ -0,0 +1,247 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap_tcpsrvr_h
+#define snap_tcpsrvr_h
+//---------------------------------------------------------------------------
+#include "snap_msgsock.h"
+#include "snap_threads.h"
+//---------------------------------------------------------------------------
+
+#define MaxWorkers 1024
+#define MaxEvents  1500
+
+const int SrvStopped = 0;
+const int SrvRunning = 1;
+const int SrvError   = 2;
+
+const longword evcServerStarted       = 0x00000001;
+const longword evcServerStopped       = 0x00000002;
+const longword evcListenerCannotStart = 0x00000004;
+const longword evcClientAdded         = 0x00000008;
+const longword evcClientRejected      = 0x00000010;
+const longword evcClientNoRoom        = 0x00000020;
+const longword evcClientException     = 0x00000040;
+const longword evcClientDisconnected  = 0x00000080;
+const longword evcClientTerminated    = 0x00000100;
+const longword evcClientsDropped      = 0x00000200;
+const longword evcReserved_00000400   = 0x00000400;
+const longword evcReserved_00000800   = 0x00000800;
+const longword evcReserved_00001000   = 0x00001000;
+const longword evcReserved_00002000   = 0x00002000;
+const longword evcReserved_00004000   = 0x00004000;
+const longword evcReserved_00008000   = 0x00008000;
+
+// Server Interface errors
+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
+
+#pragma pack(1)
+
+typedef struct{
+    time_t EvtTime;    // Timestamp
+    int EvtSender;     // Sender
+    longword EvtCode;  // Event code
+    word EvtRetCode;   // Event result
+    word EvtParam1;    // Param 1 (if available)
+    word EvtParam2;    // Param 2 (if available)
+    word EvtParam3;    // Param 3 (if available)
+    word EvtParam4;    // Param 4 (if available)
+}TSrvEvent, *PSrvEvent;
+
+extern "C" 
+{
+typedef void (S7API *pfn_SrvCallBack)(void * usrPtr, PSrvEvent PEvent, int Size);
+}
+#pragma pack()
+
+//---------------------------------------------------------------------------
+// EVENTS QUEUE
+//---------------------------------------------------------------------------
+class TMsgEventQueue
+{
+private:
+        int   IndexIn;   // <-- insert index
+        int   IndexOut;  // --> extract index
+        int   Max;       // Buffer upper bound [0..Max]
+        int   FCapacity; // Queue capacity
+        pbyte Buffer;
+        int   FBlockSize;
+public:
+        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;
+
+//---------------------------------------------------------------------------
+// WORKER THREAD
+//---------------------------------------------------------------------------
+class TCustomMsgServer; // forward declaration
+
+// It's created when connection is accepted, it will interface with the client.
+class TMsgWorkerThread : public TSnapThread
+{
+private:
+        TCustomMsgServer *FServer;
+protected:
+        TMsgSocket *WorkerSocket;
+public:
+        int Index;
+        friend class TCustomMsgServer;
+        TMsgWorkerThread(TMsgSocket *Socket, TCustomMsgServer *Server);
+        void Execute();
+};
+typedef TMsgWorkerThread *PMsgWorkerThread;
+
+//---------------------------------------------------------------------------
+// LISTENER THREAD
+//---------------------------------------------------------------------------
+// It listens for incoming connection.
+class TMsgListenerThread : public TSnapThread
+{
+private:
+        TMsgSocket *FListener;
+        TCustomMsgServer *FServer;
+public:
+        TMsgListenerThread(TMsgSocket *Listener, TCustomMsgServer *Server);
+        void Execute();
+};
+typedef TMsgListenerThread *PMsgListenerThread;
+
+//---------------------------------------------------------------------------
+// TCP SERVER
+//---------------------------------------------------------------------------
+typedef TMsgSocket *PWorkerSocket;
+
+class TCustomMsgServer
+{
+private:
+        int FLastError;
+        char FLocalAddress[16];
+        // Socket listener
+        PMsgSocket SockListener;
+        // Server listener
+        PMsgListenerThread ServerThread;
+        // Critical section to lock Workers list activities
+        PSnapCriticalSection CSList;
+        // Event queue
+        PMsgEventQueue FEventQueue;
+        // Callback related
+        pfn_SrvCallBack OnEvent;
+        void *FUsrPtr;
+        // private methods
+        int StartListener();
+        void LockList();
+        void UnlockList();
+        int FirstFree();
+protected:
+        bool Destroying;
+        // Critical section to lock Event activities
+        PSnapCriticalSection CSEvent;
+	    // Workers list
+        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,
+          word Param2, word Param3, word Param4);
+        // 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);
+public:
+        friend class TMsgWorkerThread;
+        friend class TMsgListenerThread;
+        word LocalPort;
+        longword LocalBind;
+        longword LogMask;
+        longword EventMask;
+        int Status;
+        int ClientsCount;
+        int MaxClients;
+        TCustomMsgServer();
+        virtual ~TCustomMsgServer();
+        // Starts the server
+        int Start();
+        int StartTo(const char *Address, word Port);
+        // Stops the server
+        void Stop();
+        // Sets Event callback
+        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
+{
+public:
+	bool Execute()
+	{
+            byte Buffer[4096];
+            int Size;
+
+            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;
+                }
+                else
+                    return false;
+            }
+        else
+            return true;
+	};
+};
+
+//---------------------------------------------------------------------------
+#endif // snap_tcpsrvr_h

+ 162 - 0
src/sys/snap_threads.cpp

@@ -0,0 +1,162 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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 "snap_threads.h"
+//---------------------------------------------------------------------------
+#ifdef OS_WINDOWS
+DWORD WINAPI ThreadProc(LPVOID param)
+#else
+
+void* ThreadProc(void* param)
+#endif
+{
+    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);
+#endif
+    Thread = PSnapThread(param);
+
+    if (!Thread->Terminated)
+        try
+        {
+            Thread->Execute();
+        } catch (...)
+        {
+        };
+    Thread->Closed = true;
+    if (Thread->FreeOnTerminate)
+    {
+        delete Thread;
+    };
+#ifdef OS_WINDOWS
+    ExitThread(0);
+#endif
+#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
+    pthread_exit((void*)0);
+#endif
+#if defined(OS_OSX)
+    pthread_exit((void*)0);
+#endif
+#ifdef OS_SOLARIS_NATIVE_THREADS
+    thr_exit((void*)0);
+#endif
+    return 0; // never reach, only to avoid compiler warning
+}
+//---------------------------------------------------------------------------
+TSnapThread::TSnapThread() 
+{
+    Started = false;
+	Closed=false;
+    Terminated = false;
+    FreeOnTerminate = false;
+}
+//---------------------------------------------------------------------------
+TSnapThread::~TSnapThread() 
+{
+    if (Started && !Closed)
+    {
+        Terminate();
+        Join();
+    };
+#ifdef OS_WINDOWS
+	if (Started)
+		CloseHandle(th);
+#endif
+}
+//---------------------------------------------------------------------------
+void TSnapThread::ThreadCreate() 
+{
+#ifdef OS_WINDOWS
+    th = CreateThread(0, 0, ThreadProc, this, 0, 0);
+#endif
+#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
+    pthread_attr_t a;
+    pthread_attr_init(&a);
+    pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+    pthread_create(&th, &a, &ThreadProc, this);
+#endif  
+#if defined(OS_OSX)
+    pthread_create(&th, 0, &ThreadProc, this);
+#endif
+#ifdef OS_SOLARIS_NATIVE_THREADS
+    thr_create(0, // default stack base
+               0, // default stack size
+               &ThreadProc, // Thread routine
+               this, //  argument
+               0,
+               &th);
+#endif
+}
+//---------------------------------------------------------------------------
+void TSnapThread::Start() 
+{
+    if (!Started)
+    {
+        ThreadCreate();
+        Started = true;
+    }
+}
+//---------------------------------------------------------------------------
+void TSnapThread::Terminate() 
+{
+    Terminated = true;
+}
+//---------------------------------------------------------------------------
+void TSnapThread::Kill() 
+{
+    if (Started && !Closed)
+    {
+        ThreadKill();
+        Closed = true;
+    }
+}
+//---------------------------------------------------------------------------
+void TSnapThread::Join() 
+{
+    if (Started && !Closed)
+    {
+        ThreadJoin();
+        Closed = true;
+    }
+}
+//---------------------------------------------------------------------------
+longword TSnapThread::WaitFor(uint64_t Timeout) 
+{
+    if (Started)
+    {
+        if (!Closed)
+            return ThreadWait(Timeout);
+        else
+            return WAIT_OBJECT_0;
+    }
+    else
+        return WAIT_OBJECT_0;
+}
+//---------------------------------------------------------------------------
+

+ 45 - 0
src/sys/snap_threads.h

@@ -0,0 +1,45 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|=============================================================================*/
+#ifndef snap_threads_h
+#define snap_threads_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+
+#ifdef OS_WINDOWS
+# include "win_threads.h"
+#endif
+#if defined(POSIX) && (!defined(OS_SOLARIS_NATIVE_THREADS))
+# include "unix_threads.h"
+#endif
+#ifdef OS_SOLARIS_NATIVE_THREADS
+# include "sol_threads.h"
+#endif
+#if defined(OS_OSX)
+# include "unix_threads.h"
+#endif
+
+//---------------------------------------------------------------------------
+#endif // snap_threads_h

+ 208 - 0
src/sys/sol_threads.h

@@ -0,0 +1,208 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|==============================================================================|
+|                                                                              |
+| Solaris 11 Threads support                                                   |
+|                                                                              |
+|=============================================================================*/
+#ifndef sol_threads_h
+#define sol_threads_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+#include "snap_sysutils.h"
+#include <thread.h>
+#include <synch.h>
+//---------------------------------------------------------------------------
+
+class TSnapCriticalSection {
+private:
+    mutex_t mx;
+    int result;
+public:
+
+    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 {
+private:
+    cond_t CVariable;
+    mutex_t Mutex;
+    bool AutoReset;
+    bool State;
+public:
+
+    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);
+        else
+            cond_broadcast(&CVariable);
+        mutex_unlock(&Mutex);
+    };
+
+    void Reset() 
+    {
+        mutex_lock(&Mutex);
+        State = false;
+        mutex_unlock(&Mutex);
+    }
+
+    longword WaitForever() 
+    {
+        mutex_lock(&Mutex);
+        while (!State) // <-- to avoid spurious wakeups
+            cond_wait(&CVariable, &Mutex);
+        if (AutoReset)
+            State = false;
+        mutex_unlock(&Mutex);
+        return WAIT_OBJECT_0;
+    };
+
+    longword WaitFor(int64_t Timeout) 
+    {
+        longword Result = WAIT_OBJECT_0;
+        if (Timeout == 0)
+            Timeout = 1; // 0 is not allowed 
+
+        if (Timeout > 0) {
+            mutex_lock(&Mutex);
+            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);
+                do 
+                {
+                    Result = cond_timedwait(&CVariable, &Mutex, &ts);
+                    if (Result == ETIMEDOUT)
+                        Result = WAIT_TIMEOUT;
+                } 
+                while (Result == 0 && !State);
+            } 
+            else
+                if (AutoReset) // take the ownership
+                    State = false;
+            mutex_unlock(&Mutex);
+            return Result;
+        } 
+        else // Timeout<0
+            return WaitForever();
+    };
+};
+typedef TSnapEvent *PSnapEvent;
+//---------------------------------------------------------------------------
+
+class TSnapThread {
+private:
+    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))
+            SysSleep(100);
+        if (Closed)
+            return WAIT_OBJECT_0;
+        else
+            return WAIT_TIMEOUT;
+    };
+protected:
+    bool Started;
+public:
+    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

+ 228 - 0
src/sys/unix_threads.h

@@ -0,0 +1,228 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|==============================================================================|
+|                                                                              |
+| Posix Threads support (Linux, FreeBSD)                                       |
+|                                                                              |
+|=============================================================================*/
+#ifndef unix_threads_h
+#define unix_threads_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+#include "snap_sysutils.h"
+#include <semaphore.h>
+#include <pthread.h>
+//---------------------------------------------------------------------------
+
+class TSnapCriticalSection 
+{
+private:
+    pthread_mutex_t mx;
+//    int result;
+public:
+
+    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;
+    };
+};
+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 
+{
+private:
+    pthread_cond_t CVariable;
+    pthread_mutex_t Mutex;
+    bool AutoReset;
+    bool State;
+public:
+
+    TSnapEvent(bool ManualReset) 
+    {
+        AutoReset = !ManualReset;
+        if (pthread_cond_init(&CVariable, 0) == 0)
+        	pthread_mutex_init(&Mutex, 0);
+        State = false;
+    }
+
+    ~TSnapEvent() 
+    {
+        pthread_cond_destroy(&CVariable);
+        pthread_mutex_destroy(&Mutex);
+    };
+
+    void Set() 
+    {
+        pthread_mutex_lock(&Mutex);
+        State = true;
+        if (AutoReset)
+            pthread_cond_signal(&CVariable);
+        else
+            pthread_cond_broadcast(&CVariable);
+        pthread_mutex_unlock(&Mutex);
+    };
+
+    void Reset() 
+    {
+        pthread_mutex_lock(&Mutex);
+        State = false;
+        pthread_mutex_unlock(&Mutex);
+    }
+
+    longword WaitForever() 
+    {
+        pthread_mutex_lock(&Mutex);
+        while (!State) // <-- to avoid spurious wakeups
+            pthread_cond_wait(&CVariable, &Mutex);
+        if (AutoReset)
+            State = false;
+        pthread_mutex_unlock(&Mutex);
+        return WAIT_OBJECT_0;
+    };
+
+    longword WaitFor(int64_t Timeout) 
+    {
+        longword Result = WAIT_OBJECT_0;
+        if (Timeout == 0)
+            Timeout = 1; // 0 is not allowed
+
+        if (Timeout > 0) 
+        {
+            pthread_mutex_lock(&Mutex);
+            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);
+                do {
+                    Result = pthread_cond_timedwait(&CVariable, &Mutex, &ts);
+                    if (Result == ETIMEDOUT)
+                        Result = WAIT_TIMEOUT;
+                } while (Result == 0 && !State);
+            } 
+            else
+                if (AutoReset) // take the ownership
+                    State = false;
+            pthread_mutex_unlock(&Mutex);
+            return Result;
+        }
+        else // Timeout<0
+            return WaitForever();
+    };
+};
+typedef TSnapEvent *PSnapEvent;
+//---------------------------------------------------------------------------
+class TSnapThread 
+{
+private:
+    pthread_t th;
+    bool FCreateSuspended;
+    void ThreadCreate();
+
+    void ThreadJoin() 
+    {
+        pthread_join(th, 0);
+    };
+
+    void ThreadKill() 
+    {
+        pthread_cancel(th);
+    };
+
+    longword ThreadWait(uint64_t Timeout) 
+    {
+        longword Elapsed = SysGetTick();
+        while (!Closed && !(DeltaTime(Elapsed) > Timeout))
+            SysSleep(100);
+        if (Closed)
+            return WAIT_OBJECT_0;
+        else
+            return WAIT_TIMEOUT;
+    };
+protected:
+    bool Started;
+public:
+    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 // unix_threads_h

+ 159 - 0
src/sys/win_threads.h

@@ -0,0 +1,159 @@
+/*=============================================================================|
+|  PROJECT SNAP7                                                         1.3.0 |
+|==============================================================================|
+|  Copyright (C) 2013, 2015 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/                                   |
+|==============================================================================|
+|                                                                              |
+| Windows Threads support (Windows, ReactOS)                                   |
+|                                                                              |
+|=============================================================================*/
+#ifndef win_threads_h
+#define win_threads_h
+//---------------------------------------------------------------------------
+#include "snap_platform.h"
+#include "snap_sysutils.h"
+//---------------------------------------------------------------------------
+
+class TSnapCriticalSection 
+{
+private:
+    CRITICAL_SECTION cs;
+public:
+
+    TSnapCriticalSection() 
+    {
+        InitializeCriticalSection(&cs);
+    };
+
+    ~TSnapCriticalSection() 
+    {
+        DeleteCriticalSection(&cs);
+    };
+
+    void Enter() 
+    {
+        EnterCriticalSection(&cs);
+    };
+
+    void Leave() 
+    {
+        LeaveCriticalSection(&cs);
+    };
+
+    bool TryEnter() 
+    {
+        return (TryEnterCriticalSection(&cs) != 0);
+    };
+};
+typedef TSnapCriticalSection *PSnapCriticalSection;
+//---------------------------------------------------------------------------
+
+class TSnapEvent 
+{
+private:
+    HANDLE Event;
+public:
+
+    TSnapEvent(bool ManualReset) 
+    {
+        Event = CreateEvent(0, ManualReset, false, 0);
+    };
+
+    ~TSnapEvent() 
+    {
+        if (Event != 0)
+            CloseHandle(Event);
+    };
+
+    void Set() 
+    {
+        if (Event != 0)
+            SetEvent(Event);
+    };
+
+    void Reset() 
+    {
+        if (Event != 0)
+            ResetEvent(Event);
+    };
+
+    longword WaitForever() 
+    {
+        if (Event != 0)
+            return WaitForSingleObject(Event, INFINITE);
+        else
+            return WAIT_FAILED;
+    };
+
+    longword WaitFor(int64_t Timeout) {
+        if (Event != 0)
+            return WaitForSingleObject(Event, DWORD(Timeout));
+        else
+            return WAIT_FAILED;
+    };
+};
+typedef TSnapEvent *PSnapEvent;
+//---------------------------------------------------------------------------
+
+class TSnapThread {
+private:
+    HANDLE th;
+    bool FCreateSuspended;
+    void ThreadCreate();
+
+    void ThreadJoin() 
+    {
+        WaitForSingleObject(th, INFINITE);
+    };
+
+    void ThreadKill() 
+    {
+        TerminateThread(th, 0);
+    };
+
+    longword ThreadWait(uint64_t Timeout) 
+    {
+        return WaitForSingleObject(th, DWORD(Timeout));
+    };
+protected:
+    bool Started;
+public:
+    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 // win_threads_h