/*=============================================================================|
|  PROJECT SNAP7                                                         1.2.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# Interface classes.                                                       |
|                                                                              |
|=============================================================================*/
using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Snap7
{

    public class S7Consts
    {
        #if __MonoCS__  // Assuming that we are using Unix release of Mono (otherwise modify it)
        public const string Snap7LibName = "libsnap7.so";
        #else
        public const string Snap7LibName = "snap7.dll";
        #endif
        //------------------------------------------------------------------------------
        //                                  PARAMS LIST            
        //------------------------------------------------------------------------------
        public static readonly Int32 p_u16_LocalPort     = 1;
        public static readonly Int32 p_u16_RemotePort    = 2;
        public static readonly Int32 p_i32_PingTimeout   = 3;
        public static readonly Int32 p_i32_SendTimeout   = 4;
        public static readonly Int32 p_i32_RecvTimeout   = 5;
        public static readonly Int32 p_i32_WorkInterval  = 6;
        public static readonly Int32 p_u16_SrcRef        = 7;
        public static readonly Int32 p_u16_DstRef        = 8;
        public static readonly Int32 p_u16_SrcTSap       = 9;
        public static readonly Int32 p_i32_PDURequest    = 10;
        public static readonly Int32 p_i32_MaxClients    = 11;
        public static readonly Int32 p_i32_BSendTimeout  = 12;
        public static readonly Int32 p_i32_BRecvTimeout  = 13;
        public static readonly Int32 p_u32_RecoveryTime  = 14;
        public static readonly Int32 p_u32_KeepAliveTime = 15;
    }
    
    public class S7Client
    {
        #region [Constants, private vars and TypeDefs]
        private const int MsgTextLen = 1024;
        // Error codes
        public static readonly uint errNegotiatingPDU            = 0x00100000;
        public static readonly uint errCliInvalidParams          = 0x00200000;
        public static readonly uint errCliJobPending             = 0x00300000;
        public static readonly uint errCliTooManyItems           = 0x00400000;
        public static readonly uint errCliInvalidWordLen         = 0x00500000;
        public static readonly uint errCliPartialDataWritten     = 0x00600000;
        public static readonly uint errCliSizeOverPDU            = 0x00700000;
        public static readonly uint errCliInvalidPlcAnswer       = 0x00800000;
        public static readonly uint errCliAddressOutOfRange      = 0x00900000;
        public static readonly uint errCliInvalidTransportSize   = 0x00A00000;
        public static readonly uint errCliWriteDataSizeMismatch  = 0x00B00000;
        public static readonly uint errCliItemNotAvailable       = 0x00C00000;
        public static readonly uint errCliInvalidValue           = 0x00D00000;
        public static readonly uint errCliCannotStartPLC         = 0x00E00000;
        public static readonly uint errCliAlreadyRun             = 0x00F00000;
        public static readonly uint errCliCannotStopPLC          = 0x01000000;
        public static readonly uint errCliCannotCopyRamToRom     = 0x01100000;
        public static readonly uint errCliCannotCompress         = 0x01200000;
        public static readonly uint errCliAlreadyStop            = 0x01300000;
        public static readonly uint errCliFunNotAvailable        = 0x01400000;
        public static readonly uint errCliUploadSequenceFailed   = 0x01500000;
        public static readonly uint errCliInvalidDataSizeRecvd   = 0x01600000;
        public static readonly uint errCliInvalidBlockType       = 0x01700000;
        public static readonly uint errCliInvalidBlockNumber     = 0x01800000;
        public static readonly uint errCliInvalidBlockSize       = 0x01900000;
        public static readonly uint errCliDownloadSequenceFailed = 0x01A00000;
        public static readonly uint errCliInsertRefused          = 0x01B00000;
        public static readonly uint errCliDeleteRefused          = 0x01C00000;
        public static readonly uint errCliNeedPassword           = 0x01D00000;
        public static readonly uint errCliInvalidPassword        = 0x01E00000;
        public static readonly uint errCliNoPasswordToSetOrClear = 0x01F00000;
        public static readonly uint errCliJobTimeout             = 0x02000000;
        public static readonly uint errCliPartialDataRead        = 0x02100000;
        public static readonly uint errCliBufferTooSmall         = 0x02200000;
        public static readonly uint errCliFunctionRefused        = 0x02300000;
        public static readonly uint errCliDestroying             = 0x02400000;
        public static readonly uint errCliInvalidParamNumber     = 0x02500000;
        public static readonly uint errCliCannotChangeParam      = 0x02600000;
        
        // Area ID
        public static readonly byte S7AreaPE = 0x81;
        public static readonly byte S7AreaPA = 0x82;
        public static readonly byte S7AreaMK = 0x83;
        public static readonly byte S7AreaDB = 0x84;
        public static readonly byte S7AreaCT = 0x1C;
        public static readonly byte S7AreaTM = 0x1D;

        // Word Length
        public static readonly int S7WLBit     = 0x01;
        public static readonly int S7WLByte    = 0x02;
        public static readonly int S7WLWord    = 0x04;
        public static readonly int S7WLDWord   = 0x06;
        public static readonly int S7WLReal    = 0x08;
        public static readonly int S7WLCounter = 0x1C;
        public static readonly int S7WLTimer   = 0x1D;

        // Block type
        public static readonly byte Block_OB  = 0x38;
        public static readonly byte Block_DB  = 0x41;
        public static readonly byte Block_SDB = 0x42;
        public static readonly byte Block_FC  = 0x43;
        public static readonly byte Block_SFC = 0x44;
        public static readonly byte Block_FB  = 0x45;
        public static readonly byte Block_SFB = 0x46;

        // Sub Block Type 
        public static readonly byte SubBlk_OB  = 0x08;
        public static readonly byte SubBlk_DB  = 0x0A;
        public static readonly byte SubBlk_SDB = 0x0B;
        public static readonly byte SubBlk_FC  = 0x0C;
        public static readonly byte SubBlk_SFC = 0x0D;
        public static readonly byte SubBlk_FB  = 0x0E;
        public static readonly byte SubBlk_SFB = 0x0F;

        // Block languages
        public static readonly byte BlockLangAWL   = 0x01;
        public static readonly byte BlockLangKOP   = 0x02;
        public static readonly byte BlockLangFUP   = 0x03;
        public static readonly byte BlockLangSCL   = 0x04;
        public static readonly byte BlockLangDB    = 0x05;
        public static readonly byte BlockLangGRAPH = 0x06;

        // Max number of vars (multiread/write)
        public static readonly int MaxVars = 20;

        // Client Connection Type
        public static readonly UInt16 CONNTYPE_PG    = 0x01;  // Connect to the PLC as a PG
        public static readonly UInt16 CONNTYPE_OP    = 0x02;  // Connect to the PLC as an OP
        public static readonly UInt16 CONNTYPE_BASIC = 0x03;  // Basic connection 

        // Job
        private const int JobComplete = 0;
        private const int JobPending = 1;

        private IntPtr Client;

        // Data Item 
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7DataItem
        {
            public Int32 Area;
            public Int32 WordLen;
            public Int32 Result;
            public Int32 DBNumber;
            public Int32 Start;
            public Int32 Amount;
            public IntPtr pData;
        }

        // Block List
        [StructLayout(LayoutKind.Sequential, Pack = 1)] // <- "maybe" we don't need
        public struct S7BlocksList
        {
            public Int32 OBCount;
            public Int32 FBCount;
            public Int32 FCCount;
            public Int32 SFBCount;
            public Int32 SFCCount;
            public Int32 DBCount;
            public Int32 SDBCount;
        };

        // Packed Block Info
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        protected struct US7BlockInfo
        {
            public Int32 BlkType;
            public Int32 BlkNumber;
            public Int32 BlkLang;
            public Int32 BlkFlags;
            public Int32 MC7Size;  // The real size in bytes
            public Int32 LoadSize;
            public Int32 LocalData;
            public Int32 SBBLength;
            public Int32 CheckSum;
            public Int32 Version;
            // Chars info
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
            public char[] CodeDate;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
            public char[] IntfDate;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
            public char[] Author;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
            public char[] Family;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
            public char[] Header;
        };
        private US7BlockInfo UBlockInfo;

        // Managed Block Info
        public struct S7BlockInfo
        {
            public int BlkType;
            public int BlkNumber;
            public int BlkLang;
            public int BlkFlags;
            public int MC7Size;  // The real size in bytes
            public int LoadSize;
            public int LocalData;
            public int SBBLength;
            public int CheckSum;
            public int Version;
            // Chars info
            public string CodeDate;
            public string IntfDate;
            public string Author;
            public string Family;
            public string Header;
        };


        public ushort[] TS7BlocksOfType;

        // Packed Order Code + Version
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        protected struct US7OrderCode
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 21)]
            public char[] Code;
            public byte V1;
            public byte V2;
            public byte V3;
        };
        private US7OrderCode UOrderCode;

        // Managed Order Code + Version
        public struct S7OrderCode
        {
            public string Code;
            public byte V1;
            public byte V2;
            public byte V3;
        };

        // Packed CPU Info
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        protected struct US7CpuInfo
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 33)]
            public char[] ModuleTypeName;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public char[] SerialNumber;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public char[] ASName;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 27)]
            public char[] Copyright;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public char[] ModuleName;
        };
        private US7CpuInfo UCpuInfo;

        // Managed CPU Info
        public struct S7CpuInfo
        {
            public string ModuleTypeName;
            public string SerialNumber;
            public string ASName;
            public string Copyright;
            public string ModuleName;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7CpInfo
        {
            public Int32 MaxPduLengt;
            public Int32 MaxConnections;
            public Int32 MaxMpiRate;
            public Int32 MaxBusRate;
        };

        // See §33.1 of "System Software for S7-300/400 System and Standard Functions"
        // and see SFC51 description too
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct SZL_HEADER
        {
            public UInt16 LENTHDR;
            public UInt16 N_DR;
        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7SZL
        {
            public SZL_HEADER Header;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000 - 4)]
            public byte[] Data;
        };

        // SZL List of available SZL IDs : same as SZL but List items are big-endian adjusted
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7SZLList
        {
            public SZL_HEADER Header;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2000 - 2)]
            public UInt16[] Data;
        };

        // S7 Protection
        // See §33.19 of "System Software for S7-300/400 System and Standard Functions"
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7Protection  // Packed S7Protection
        {
            public UInt16 sch_schal;
            public UInt16 sch_par;
            public UInt16 sch_rel;
            public UInt16 bart_sch;
            public UInt16 anl_sch;
        };

        // C++ time struct, functions to convert it from/to DateTime are provided ;-)
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        protected struct cpp_tm
        {
            public Int32 tm_sec;
            public Int32 tm_min;
            public Int32 tm_hour;
            public Int32 tm_mday;
            public Int32 tm_mon;
            public Int32 tm_year;
            public Int32 tm_wday;
            public Int32 tm_yday;
            public Int32 tm_isdst;
        }
        private cpp_tm tm;

        #endregion

        #region [Class Control]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern IntPtr Cli_Create();
        public S7Client()
        {
            Client = Cli_Create();
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Destroy(ref IntPtr Client);
        ~S7Client()
        {
            Cli_Destroy(ref Client);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Connect(IntPtr Client);
        public int Connect()
        {
            return Cli_Connect(Client);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ConnectTo(IntPtr Client,
            [MarshalAs(UnmanagedType.LPStr)] string Address,
            int Rack,
            int Slot
            );
        
        public int ConnectTo(string Address, int Rack, int Slot)
        {
            return Cli_ConnectTo(Client, Address, Rack, Slot);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetConnectionParams(IntPtr Client,
            [MarshalAs(UnmanagedType.LPStr)] string Address,
            UInt16 LocalTSAP,
            UInt16 RemoteTSAP
            );

        public int SetConnectionParams(string Address, UInt16 LocalTSAP, UInt16 RemoteTSAP)
        {
            return Cli_SetConnectionParams(Client, Address, LocalTSAP, RemoteTSAP);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetConnectionType(IntPtr Client, UInt16 ConnectionType);
        public int SetConnectionType(UInt16 ConnectionType)
        {
            return Cli_SetConnectionType(Client, ConnectionType);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Disconnect(IntPtr Client);
        public int Disconnect()
        {
            return Cli_Disconnect(Client);
        }

        // Get/SetParam needs a void* parameter, internally it decides the kind of pointer
        // in accord to ParamNumber.
        // To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_i16(IntPtr Client, Int32 ParamNumber, ref Int16 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Cli_GetParam_i16(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_u16(IntPtr Client, Int32 ParamNumber, ref UInt16 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Cli_GetParam_u16(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_i32(IntPtr Client, Int32 ParamNumber, ref Int32 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Cli_GetParam_i32(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_u32(IntPtr Client, Int32 ParamNumber, ref UInt32 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Cli_GetParam_u32(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_i64(IntPtr Client, Int32 ParamNumber, ref Int64 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Cli_GetParam_i64(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_GetParam")]
        protected static extern int Cli_GetParam_u64(IntPtr Client, Int32 ParamNumber, ref UInt64 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Cli_GetParam_u64(Client, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_i16(IntPtr Client, Int32 ParamNumber, ref Int16 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Cli_SetParam_i16(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_u16(IntPtr Client, Int32 ParamNumber, ref UInt16 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Cli_SetParam_u16(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_i32(IntPtr Client, Int32 ParamNumber, ref Int32 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Cli_SetParam_i32(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_u32(IntPtr Client, Int32 ParamNumber, ref UInt32 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Cli_SetParam_u32(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_i64(IntPtr Client, Int32 ParamNumber, ref Int64 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Cli_SetParam_i64(Client, ParamNumber, ref IntValue);
        }
        
        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Cli_SetParam")]
        protected static extern int Cli_SetParam_u64(IntPtr Client, Int32 ParamNumber, ref UInt64 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Cli_SetParam_u64(Client, ParamNumber, ref IntValue);
        }

        public delegate void S7CliCompletion(IntPtr usrPtr, int opCode, int opResult);

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetAsCallback(IntPtr Client, S7CliCompletion Completion, IntPtr usrPtr);
        public int SetAsCallBack(S7CliCompletion Completion, IntPtr usrPtr)
        {
            return Cli_SetAsCallback(Client, Completion, usrPtr);
        }
        
        #endregion

        #region [Data I/O main functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ReadArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
        public int ReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
        {
            return Cli_ReadArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_WriteArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
        public int WriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
        {
            return Cli_WriteArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ReadMultiVars(IntPtr Client, ref S7DataItem Item, int ItemsCount);
        public int ReadMultiVars(ref S7DataItem Item, int ItemsCount)
        {
            return Cli_ReadMultiVars(Client, ref Item, ItemsCount);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_WriteMultiVars(IntPtr Client, ref S7DataItem Item, int ItemsCount);
        public int WriteMultiVars(ref S7DataItem Item, int ItemsCount)
        {
            return Cli_WriteMultiVars(Client, ref Item, ItemsCount);
        }

        #endregion

        #region [Data I/O lean functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_DBRead(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
        public int DBRead(int DBNumber, int Start, int Size, byte[] Buffer)
        {
            return Cli_DBRead(Client, DBNumber, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_DBWrite(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
        public int DBWrite(int DBNumber, int Start, int Size, byte[] Buffer)
        {
            return Cli_DBWrite(Client, DBNumber, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_MBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int MBRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_MBRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_MBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int MBWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_MBWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_EBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int EBRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_EBRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_EBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int EBWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_EBWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ABRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int ABRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_ABRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ABWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int ABWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_ABWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_TMRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int TMRead(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_TMRead(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_TMWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int TMWrite(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_TMWrite(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_CTRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int CTRead(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_CTRead(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_CTWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int CTWrite(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_CTWrite(Client, Start, Amount, Buffer);
        }

        #endregion

        #region [Directory functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ListBlocks(IntPtr Client, ref S7BlocksList List);
        public int ListBlocks(ref S7BlocksList List)
        {
            return Cli_ListBlocks(Client, ref List);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetAgBlockInfo(IntPtr Client, int BlockType, int BlockNum, ref US7BlockInfo Info);
        public int GetAgBlockInfo(IntPtr Client, int BlockType, int BlockNum, ref S7BlockInfo Info)
        {
            int res = Cli_GetAgBlockInfo(Client, BlockType, BlockNum, ref UBlockInfo);
            // Packed->Managed
            if (res == 0)
            {
                Info.BlkType = UBlockInfo.BlkType;
                Info.BlkNumber = UBlockInfo.BlkNumber;
                Info.BlkLang = UBlockInfo.BlkLang;
                Info.BlkFlags = UBlockInfo.BlkFlags;
                Info.MC7Size = UBlockInfo.MC7Size;
                Info.LoadSize = UBlockInfo.LoadSize;
                Info.LocalData = UBlockInfo.LocalData;
                Info.SBBLength = UBlockInfo.SBBLength;
                Info.CheckSum = UBlockInfo.CheckSum;
                Info.Version = UBlockInfo.Version;
                // Chars info
                Info.CodeDate = new string(UBlockInfo.CodeDate);
                Info.IntfDate = new string(UBlockInfo.IntfDate);
                Info.Author = new string(UBlockInfo.Author);
                Info.Family = new string(UBlockInfo.Family);
                Info.Header = new string(UBlockInfo.Header);
            }
            return res;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetPgBlockInfo(IntPtr Client, ref US7BlockInfo Info, byte[] Buffer, Int32 Size);
        public int GetPgBlockInfo(ref S7BlockInfo Info, byte[] Buffer, int Size)
        {
            int res = Cli_GetPgBlockInfo(Client, ref UBlockInfo, Buffer, Size);
            // Packed->Managed
            if (res == 0)
            {
                Info.BlkType = UBlockInfo.BlkType;
                Info.BlkNumber = UBlockInfo.BlkNumber;
                Info.BlkLang = UBlockInfo.BlkLang;
                Info.BlkFlags = UBlockInfo.BlkFlags;
                Info.MC7Size = UBlockInfo.MC7Size;
                Info.LoadSize = UBlockInfo.LoadSize;
                Info.LocalData = UBlockInfo.LocalData;
                Info.SBBLength = UBlockInfo.SBBLength;
                Info.CheckSum = UBlockInfo.CheckSum;
                Info.Version = UBlockInfo.Version;
                // Chars info
                Info.CodeDate = new string(UBlockInfo.CodeDate);
                Info.IntfDate = new string(UBlockInfo.IntfDate);
                Info.Author = new string(UBlockInfo.Author);
                Info.Family = new string(UBlockInfo.Family);
                Info.Header = new string(UBlockInfo.Header);
            }
            return res;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ListBlocksOfType(IntPtr Client, Int32 BlockType, ushort[] List, ref int ItemsCount);
        public int ListBlocksOfType(int BlockType, ushort[] List, ref int ItemsCount)
        {
            return Cli_ListBlocksOfType(Client, BlockType, List, ref ItemsCount);
        }

        #endregion

        #region [Blocks functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Upload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
        public int Upload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
        {
            return Cli_Upload(Client, BlockType, BlockNum, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_FullUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
        public int FullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
        {
            return Cli_FullUpload(Client, BlockType, BlockNum, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Download(IntPtr Client, int BlockNum, byte[] UsrData, int Size);
        public int Download(int BlockNum, byte[] UsrData, int Size)
        {
            return Cli_Download(Client, BlockNum, UsrData, Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Delete(IntPtr Client, int BlockType, int BlockNum);
        public int Delete(int BlockType, int BlockNum)
        {
            return Cli_Delete(Client, BlockType, BlockNum);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_DBGet(IntPtr Client, int DBNumber, byte[] UsrData, ref int Size);
        public int DBGet(int DBNumber, byte[] UsrData, ref int Size)
        {
            return Cli_DBGet(Client, DBNumber, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_DBFill(IntPtr Client, int DBNumber, int FillChar);
        public int DBFill(int DBNumber, int FillChar)
        {
            return Cli_DBFill(Client, DBNumber, FillChar);
        }

        #endregion

        #region [Date/Time functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetPlcDateTime(IntPtr Client, ref cpp_tm tm);
        public int GetPlcDateTime(ref DateTime DT)
        {           
            int res = Cli_GetPlcDateTime(Client, ref tm);
            if (res == 0)
            {
                // Packed->Managed
                DateTime PlcDT = new DateTime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
                DT = PlcDT;
            }
            return res;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetPlcDateTime(IntPtr Client, ref cpp_tm tm);
        public int SetPlcDateTime(DateTime DT)
        {

            // Managed->Packed
            tm.tm_year = DT.Year - 1900;
            tm.tm_mon = DT.Month-1;
            tm.tm_mday = DT.Day;
            tm.tm_hour = DT.Hour;
            tm.tm_min = DT.Minute;
            tm.tm_sec = DT.Second;

            return Cli_SetPlcDateTime(Client, ref tm);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetPlcSystemDateTime(IntPtr Client);
        public int SetPlcSystemDateTime()
        {
            return Cli_SetPlcSystemDateTime(Client);
        }      
        
        #endregion

        #region [System Info functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetOrderCode(IntPtr Client, ref US7OrderCode Info);
        public int GetOrderCode(ref S7OrderCode Info)
        {
            int res = Cli_GetOrderCode(Client, ref UOrderCode);
            // Packed->Managed
            if (res == 0)
            {
                Info.Code = new string(UOrderCode.Code);
                Info.V1 = UOrderCode.V1;
                Info.V2 = UOrderCode.V2;
                Info.V3 = UOrderCode.V3;
            }
            return res;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetCpuInfo(IntPtr Client, ref US7CpuInfo Info);
        public int GetCpuInfo(ref S7CpuInfo Info)
        {
            int res = Cli_GetCpuInfo(Client, ref UCpuInfo);
            // Packed->Managed
            if (res == 0)
            {
                Info.ModuleTypeName = new string(UCpuInfo.ModuleTypeName);
                Info.SerialNumber = new string(UCpuInfo.SerialNumber);
                Info.ASName = new string(UCpuInfo.ASName);
                Info.Copyright = new string(UCpuInfo.Copyright);
                Info.ModuleName = new string(UCpuInfo.ModuleName);
            }
            return res;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetCpInfo(IntPtr Client, ref S7CpInfo Info);

        public int GetCpInfo(ref S7CpInfo Info)
        {
            return Cli_GetCpInfo(Client, ref Info);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ReadSZL(IntPtr Client, int ID, int Index, ref S7SZL Data, ref Int32 Size);
        public int ReadSZL(int ID, int Index, ref S7SZL Data, ref Int32 Size)
        {
            return Cli_ReadSZL(Client, ID, Index, ref Data, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ReadSZLList(IntPtr Client, ref S7SZLList List, ref Int32 ItemsCount);
        public int ReadSZLList(ref S7SZLList List, ref Int32 ItemsCount)
        {
            return Cli_ReadSZLList(Client, ref List, ref ItemsCount);
        }

        #endregion

        #region [Control functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_PlcHotStart(IntPtr Client);
        public int PlcHotStart()
        {
            return Cli_PlcHotStart(Client);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_PlcColdStart(IntPtr Client);
        public int PlcColdStart()
        {
            return Cli_PlcColdStart(Client);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_PlcStop(IntPtr Client);
        public int PlcStop()
        {
            return Cli_PlcStop(Client);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_CopyRamToRom(IntPtr Client, UInt32 Timeout);
        public int PlcCopyRamToRom(UInt32 Timeout)
        {
            return Cli_CopyRamToRom(Client, Timeout);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_Compress(IntPtr Client, UInt32 Timeout);
        public int PlcCompress(UInt32 Timeout)
        {
            return Cli_Compress(Client, Timeout);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetPlcStatus(IntPtr Client, ref Int32 Status);
        public int PlcGetStatus(ref Int32 Status)
        {
            return Cli_GetPlcStatus(Client, ref Status);
        }

        #endregion

        #region [Security functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetProtection(IntPtr Client, ref S7Protection Protection);
        public int GetProtection(ref S7Protection Protection)
        {
            return Cli_GetProtection(Client, ref Protection);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_SetSessionPassword(IntPtr Client, [MarshalAs(UnmanagedType.LPStr)] string Password);
        public int SetSessionPassword(string Password)
        {
            return Cli_SetSessionPassword(Client, Password);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_ClearSessionPassword(IntPtr Client);
        public int ClearSessionPassword()
        {
            return Cli_ClearSessionPassword(Client);
        }

        #endregion

        #region [Low Level]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_IsoExchangeBuffer(IntPtr Client, byte[] Buffer, ref Int32 Size);
        public int IsoExchangeBuffer(byte[] Buffer, ref Int32 Size)
        {
            return Cli_IsoExchangeBuffer(Client, Buffer, ref Size);
        }

        #endregion

        #region [Async functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsReadArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
        public int AsReadArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
        {
            return Cli_AsReadArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsWriteArea(IntPtr Client, int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer);
        public int AsWriteArea(int Area, int DBNumber, int Start, int Amount, int WordLen, byte[] Buffer)
        {
            return Cli_AsWriteArea(Client, Area, DBNumber, Start, Amount, WordLen, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsDBRead(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
        public int AsDBRead(int DBNumber, int Start, int Size, byte[] Buffer)
        {
            return Cli_AsDBRead(Client, DBNumber, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsDBWrite(IntPtr Client, int DBNumber, int Start, int Size, byte[] Buffer);
        public int AsDBWrite(int DBNumber, int Start, int Size, byte[] Buffer)
        {
            return Cli_AsDBWrite(Client, DBNumber, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsMBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsMBRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsMBRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsMBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsMBWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsMBWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsEBRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsEBRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsEBRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsEBWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsEBWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsEBWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsABRead(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsABRead(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsABRead(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsABWrite(IntPtr Client, int Start, int Size, byte[] Buffer);
        public int AsABWrite(int Start, int Size, byte[] Buffer)
        {
            return Cli_AsABWrite(Client, Start, Size, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsTMRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int AsTMRead(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_AsTMRead(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsTMWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int AsTMWrite(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_AsTMWrite(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsCTRead(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int AsCTRead(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_AsCTRead(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsCTWrite(IntPtr Client, int Start, int Amount, ushort[] Buffer);
        public int AsCTWrite(int Start, int Amount, ushort[] Buffer)
        {
            return Cli_AsCTWrite(Client, Start, Amount, Buffer);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsListBlocksOfType(IntPtr Client, Int32 BlockType, ushort[] List);
        public int AsListBlocksOfType(int BlockType, ushort[] List)
        {
            return Cli_AsListBlocksOfType(Client, BlockType, List);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsReadSZL(IntPtr Client, int ID, int Index, ref S7SZL Data, ref Int32 Size);
        public int AsReadSZL(int ID, int Index, ref S7SZL Data, ref Int32 Size)
        {
            return Cli_AsReadSZL(Client, ID, Index, ref Data, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsReadSZLList(IntPtr Client, ref S7SZLList List, ref Int32 ItemsCount);
        public int AsReadSZLList(ref S7SZLList List, ref Int32 ItemsCount)
        {
            return Cli_AsReadSZLList(Client, ref List, ref ItemsCount);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
        public int AsUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
        {
            return Cli_AsUpload(Client, BlockType, BlockNum, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsFullUpload(IntPtr Client, int BlockType, int BlockNum, byte[] UsrData, ref int Size);
        public int AsFullUpload(int BlockType, int BlockNum, byte[] UsrData, ref int Size)
        {
            return Cli_AsFullUpload(Client, BlockType, BlockNum, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsDownload(IntPtr Client, int BlockNum, byte[] UsrData, int Size);
        public int ASDownload(int BlockNum, byte[] UsrData, int Size)
        {
            return Cli_AsDownload(Client, BlockNum, UsrData, Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsPlcCopyRamToRom(IntPtr Client, UInt32 Timeout);
        public int AsPlcCopyRamToRom(UInt32 Timeout)
        {
            return Cli_AsPlcCopyRamToRom(Client, Timeout);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsPlcCompress(IntPtr Client, UInt32 Timeout);
        public int AsPlcCompress(UInt32 Timeout)
        {
            return Cli_AsPlcCompress(Client, Timeout);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsDBGet(IntPtr Client, int DBNumber, byte[] UsrData, ref int Size);
        public int AsDBGet(int DBNumber, byte[] UsrData, ref int Size)
        {
            return Cli_AsDBGet(Client, DBNumber, UsrData, ref Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_AsDBFill(IntPtr Client, int DBNumber, int FillChar);
        public int AsDBFill(int DBNumber, int FillChar)
        {
            return Cli_AsDBFill(Client, DBNumber, FillChar);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_CheckAsCompletion(IntPtr Client, ref Int32 opResult);
        public bool CheckAsCompletion(ref int opResult)
        {
            return Cli_CheckAsCompletion(Client, ref opResult) == JobComplete;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_WaitAsCompletion(IntPtr Client, Int32 Timeout);
        public int WaitAsCompletion(int Timeout)
        {
            return Cli_WaitAsCompletion(Client, Timeout);
        }

        #endregion

        #region [Info Functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetExecTime(IntPtr Client, ref UInt32 Time);
        public int ExecTime()
        {
            UInt32 Time = new UInt32();
            if (Cli_GetExecTime(Client, ref Time) == 0)
                return (int)(Time);
            else
                return -1;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetLastError(IntPtr Client, ref Int32 LastError);
        public int LastError()
        {
            Int32 ClientLastError = new Int32();
            if (Cli_GetLastError(Client, ref ClientLastError) == 0)
                return (int)ClientLastError;
            else
                return -1;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetPduLength(IntPtr Client, ref Int32 Requested, ref Int32 Negotiated);

        public int RequestedPduLength()
        {
            Int32 Requested = new Int32();
            Int32 Negotiated = new Int32();
            if (Cli_GetPduLength(Client, ref Requested, ref Negotiated) == 0)
                return Requested;
            else
                return -1;
        }

        public int NegotiatedPduLength()
        {
            Int32 Requested = new Int32();
            Int32 Negotiated = new Int32();
            if (Cli_GetPduLength(Client, ref Requested, ref Negotiated) == 0)
                return Negotiated;
            else
                return -1;
        }

        [DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
        protected static extern int Cli_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
        public string ErrorText(int Error)
        {
            StringBuilder Message = new StringBuilder(MsgTextLen);
            Cli_ErrorText(Error, Message, MsgTextLen);
            return Message.ToString();
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Cli_GetConnected(IntPtr Client, ref UInt32 IsConnected);
        public bool Connected()
        {
            UInt32 IsConnected = new UInt32();
            if (Cli_GetConnected(Client, ref IsConnected) == 0)
                return IsConnected!=0;
            else
                return false;
        }

        #endregion
    }

    public class S7Server
    {
        #region [Constants, private vars and TypeDefs]

        private const int MsgTextLen = 1024;
        private const int mkEvent = 0;
        private const int mkLog   = 1;

        // Server Area ID  (use with Register/unregister - Lock/unlock Area)
        public static readonly int srvAreaPE = 0;
        public static readonly int srvAreaPA = 1;
        public static readonly int srvAreaMK = 2;
        public static readonly int srvAreaCT = 3;
        public static readonly int srvAreaTM = 4;
        public static readonly int srvAreaDB = 5;
        // Errors
        public static readonly uint errSrvCannotStart        = 0x00100000; // Server cannot start
        public static readonly uint errSrvDBNullPointer      = 0x00200000; // Passed null as PData
        public static readonly uint errSrvAreaAlreadyExists  = 0x00300000; // Area Re-registration
        public static readonly uint errSrvUnknownArea        = 0x00400000; // Unknown area
        public static readonly uint errSrvInvalidParams      = 0x00500000; // Invalid param(s) supplied
        public static readonly uint errSrvTooManyDB          = 0x00600000; // Cannot register DB
        public static readonly uint errSrvInvalidParamNumber = 0x00700000; // Invalid param (srv_get/set_param)
        public static readonly uint errSrvCannotChangeParam  = 0x00800000; // Cannot change because running

        // TCP Server Event codes
        public static readonly uint evcServerStarted       = 0x00000001;
        public static readonly uint evcServerStopped       = 0x00000002;
        public static readonly uint evcListenerCannotStart = 0x00000004;
        public static readonly uint evcClientAdded         = 0x00000008;
        public static readonly uint evcClientRejected      = 0x00000010;
        public static readonly uint evcClientNoRoom        = 0x00000020;
        public static readonly uint evcClientException     = 0x00000040;
        public static readonly uint evcClientDisconnected  = 0x00000080;
        public static readonly uint evcClientTerminated    = 0x00000100;
        public static readonly uint evcClientsDropped      = 0x00000200;
        public static readonly uint evcReserved_00000400   = 0x00000400; // actually unused
        public static readonly uint evcReserved_00000800   = 0x00000800; // actually unused
        public static readonly uint evcReserved_00001000   = 0x00001000; // actually unused
        public static readonly uint evcReserved_00002000   = 0x00002000; // actually unused
        public static readonly uint evcReserved_00004000   = 0x00004000; // actually unused
        public static readonly uint evcReserved_00008000   = 0x00008000; // actually unused
        // S7 Server Event Code
        public static readonly uint evcPDUincoming         = 0x00010000;
        public static readonly uint evcDataRead            = 0x00020000;
        public static readonly uint evcDataWrite           = 0x00040000;
        public static readonly uint evcNegotiatePDU        = 0x00080000;
        public static readonly uint evcReadSZL             = 0x00100000;
        public static readonly uint evcClock               = 0x00200000;
        public static readonly uint evcUpload              = 0x00400000;
        public static readonly uint evcDownload            = 0x00800000;
        public static readonly uint evcDirectory           = 0x01000000;
        public static readonly uint evcSecurity            = 0x02000000;
        public static readonly uint evcControl             = 0x04000000;
        public static readonly uint evcReserved_08000000   = 0x08000000; // actually unused
        public static readonly uint evcReserved_10000000   = 0x10000000; // actually unused
        public static readonly uint evcReserved_20000000   = 0x20000000; // actually unused
        public static readonly uint evcReserved_40000000   = 0x40000000; // actually unused
        public static readonly uint evcReserved_80000000   = 0x80000000; // actually unused
        // Masks to enable/disable all events
        public static readonly uint evcAll  = 0xFFFFFFFF;
        public static readonly uint evcNone = 0x00000000;
        // Event SubCodes
        public static readonly ushort evsUnknown       = 0x0000;
        public static readonly ushort evsStartUpload   = 0x0001;
        public static readonly ushort evsStartDownload = 0x0001;
        public static readonly ushort evsGetBlockList  = 0x0001;
        public static readonly ushort evsStartListBoT  = 0x0002;
        public static readonly ushort evsListBoT       = 0x0003;
        public static readonly ushort evsGetBlockInfo  = 0x0004;
        public static readonly ushort evsGetClock      = 0x0001;
        public static readonly ushort evsSetClock      = 0x0002;
        public static readonly ushort evsSetPassword   = 0x0001;
        public static readonly ushort evsClrPassword   = 0x0002;
        // Event Params : functions group
        public static readonly ushort grProgrammer     = 0x0041;
        public static readonly ushort grCyclicData     = 0x0042;
        public static readonly ushort grBlocksInfo     = 0x0043;
        public static readonly ushort grSZL            = 0x0044;
        public static readonly ushort grPassword       = 0x0045;
        public static readonly ushort grBSend          = 0x0046;
        public static readonly ushort grClock          = 0x0047;
        public static readonly ushort grSecurity       = 0x0045;
        // Event Params : control codes
        public static readonly ushort CodeControlUnknown   = 0x0000;
        public static readonly ushort CodeControlColdStart = 0x0001;
        public static readonly ushort CodeControlWarmStart = 0x0002;
        public static readonly ushort CodeControlStop      = 0x0003;
        public static readonly ushort CodeControlCompress  = 0x0004;
        public static readonly ushort CodeControlCpyRamRom = 0x0005;
        public static readonly ushort CodeControlInsDel    = 0x0006;
        // Event Result
        public static readonly ushort evrNoError = 0x0000;
        public static readonly ushort evrFragmentRejected  = 0x0001;
        public static readonly ushort evrMalformedPDU      = 0x0002;
        public static readonly ushort evrSparseBytes       = 0x0003;
        public static readonly ushort evrCannotHandlePDU   = 0x0004;
        public static readonly ushort evrNotImplemented    = 0x0005;
        public static readonly ushort evrErrException      = 0x0006;
        public static readonly ushort evrErrAreaNotFound   = 0x0007;
        public static readonly ushort evrErrOutOfRange     = 0x0008;
        public static readonly ushort evrErrOverPDU        = 0x0009;
        public static readonly ushort evrErrTransportSize  = 0x000A;
        public static readonly ushort evrInvalidGroupUData = 0x000B;
        public static readonly ushort evrInvalidSZL        = 0x000C;
        public static readonly ushort evrDataSizeMismatch  = 0x000D;
        public static readonly ushort evrCannotUpload      = 0x000E;
        public static readonly ushort evrCannotDownload    = 0x000F;
        public static readonly ushort evrUploadInvalidID   = 0x0010;
        public static readonly ushort evrResNotFound       = 0x0011;

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct USrvEvent
        {
            public IntPtr EvtTime;   // It's platform dependent (32 or 64 bit)
            public Int32  EvtSender;
            public UInt32 EvtCode;
            public ushort EvtRetCode;
            public ushort EvtParam1;
            public ushort EvtParam2;
            public ushort EvtParam3;
            public ushort EvtParam4;
        }

        public struct SrvEvent
        {
            public DateTime EvtTime;
            public Int32  EvtSender;
            public UInt32 EvtCode;
            public ushort EvtRetCode;
            public ushort EvtParam1;
            public ushort EvtParam2;
            public ushort EvtParam3;
            public ushort EvtParam4;
        }

        private IntPtr Server;

        #endregion

        #region [Class Control]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern IntPtr Srv_Create();
        public S7Server()
        {
            Server = Srv_Create();
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_Destroy(ref IntPtr Server);
        ~S7Server()
        {
            Srv_Destroy(ref Server);
        }
        
        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_StartTo(IntPtr Server, [MarshalAs(UnmanagedType.LPStr)] string Address);
        public int StartTo(string Address)
        {
            return Srv_StartTo(Server, Address);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_Start(IntPtr Server);
        public int Start()
        {
            return Srv_Start(Server);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_Stop(IntPtr Server);
        public int Stop()
        {
            return Srv_Stop(Server);
        }

        // Get/SetParam needs a void* parameter, internally it decides the kind of pointer
        // in accord to ParamNumber.
        // To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_i16(IntPtr Server, Int32 ParamNumber, ref Int16 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Srv_GetParam_i16(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_u16(IntPtr Server, Int32 ParamNumber, ref UInt16 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Srv_GetParam_u16(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_i32(IntPtr Server, Int32 ParamNumber, ref Int32 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Srv_GetParam_i32(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_u32(IntPtr Server, Int32 ParamNumber, ref UInt32 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Srv_GetParam_u32(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_i64(IntPtr Server, Int32 ParamNumber, ref Int64 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Srv_GetParam_i64(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_GetParam")]
        protected static extern int Srv_GetParam_u64(IntPtr Server, Int32 ParamNumber, ref UInt64 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Srv_GetParam_u64(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_i16(IntPtr Server, Int32 ParamNumber, ref Int16 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Srv_SetParam_i16(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_u16(IntPtr Server, Int32 ParamNumber, ref UInt16 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Srv_SetParam_u16(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_i32(IntPtr Server, Int32 ParamNumber, ref Int32 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Srv_SetParam_i32(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_u32(IntPtr Server, Int32 ParamNumber, ref UInt32 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Srv_SetParam_u32(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_i64(IntPtr Server, Int32 ParamNumber, ref Int64 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Srv_SetParam_i64(Server, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Srv_SetParam")]
        protected static extern int Srv_SetParam_u64(IntPtr Server, Int32 ParamNumber, ref UInt64 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Srv_SetParam_u64(Server, ParamNumber, ref IntValue);
        }
        
        #endregion

        #region [Data Areas functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_RegisterArea(IntPtr Server, Int32 AreaCode, Int32 Index, byte[] pUsrData, Int32 Size);
        public int RegisterArea(Int32 AreaCode, Int32 Index, byte[] pUsrData, Int32 Size)
        {
            return Srv_RegisterArea(Server, AreaCode, Index, pUsrData, Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_UnregisterArea(IntPtr Server, Int32 AreaCode, Int32 Index);
        public int UnregisterArea(Int32 AreaCode, Int32 Index)
        {
            return Srv_UnregisterArea(Server, AreaCode, Index);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_LockArea(IntPtr Server, Int32 AreaCode, Int32 Index);
        public int LockArea(Int32 AreaCode, Int32 Index)
        {
            return Srv_LockArea(Server, AreaCode, Index);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_UnlockArea(IntPtr Server, Int32 AreaCode, Int32 Index);
        public int UnlockArea(Int32 AreaCode, Int32 Index)
        {
            return Srv_UnlockArea(Server, AreaCode, Index);
        }

        #endregion

        #region [Notification functions (Events)]

        public delegate void TSrvCallback(IntPtr usrPtr, ref USrvEvent Event, int Size);

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_SetEventsCallback(IntPtr Server, TSrvCallback Callback, IntPtr usrPtr);
        public int SetEventsCallBack(TSrvCallback Callback, IntPtr usrPtr)
        {
            return Srv_SetEventsCallback(Server, Callback, usrPtr);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_SetReadEventsCallback(IntPtr Server, TSrvCallback Callback, IntPtr usrPtr);
        public int SetReadEventsCallBack(TSrvCallback Callback, IntPtr usrPtr)
        {
            return Srv_SetReadEventsCallback(Server, Callback, usrPtr);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_PickEvent(IntPtr Server, ref USrvEvent Event, ref Int32 EvtReady);
        public bool PickEvent(ref USrvEvent Event)
        {
            Int32 EvtReady = new Int32();
            if (Srv_PickEvent(Server, ref Event, ref EvtReady) == 0)
                return EvtReady != 0;
            else
                return false;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_ClearEvents(IntPtr Server);
        public int ClearEvents()
        {
            return Srv_ClearEvents(Server);
        }
        
        [DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
        protected static extern int Srv_EventText(ref USrvEvent Event, StringBuilder EvtMsg, int TextSize);
        public string EventText(ref USrvEvent Event)
        {
            StringBuilder Message = new StringBuilder(MsgTextLen);
            Srv_EventText(ref Event, Message, MsgTextLen);
            return Message.ToString();
        }

        public DateTime EvtTimeToDateTime(IntPtr TimeStamp)
        {
            DateTime UnixStartEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return UnixStartEpoch.AddSeconds(Convert.ToDouble(TimeStamp));
        }        
        
        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_GetMask(IntPtr Server, Int32 MaskKind, ref UInt32 Mask);
        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_SetMask(IntPtr Server, Int32 MaskKind, UInt32 Mask);
        
        // Property LogMask R/W
        public UInt32 LogMask {
            get 
            {
                UInt32 Mask = new UInt32();
                if (Srv_GetMask(Server, S7Server.mkLog, ref Mask)==0)
                    return Mask;
                else
                    return 0;
            }
            set
            {
                Srv_SetMask(Server, S7Server.mkLog, value);
            }
        }

        // Property EventMask R/W
        public UInt32 EventMask
        {
            get
            {
                UInt32 Mask = new UInt32();
                if (Srv_GetMask(Server, S7Server.mkEvent, ref Mask) == 0)
                    return Mask;
                else
                    return 0;
            }
            set
            {
                Srv_SetMask(Server, S7Server.mkEvent, value);
            }
        }


        #endregion

        #region [Info functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_GetStatus(IntPtr Server, ref Int32 ServerStatus, ref Int32 CpuStatus, ref Int32 ClientsCount);
        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Srv_SetCpuStatus(IntPtr Server, Int32 CpuStatus);       
        
        // Property Virtual CPU status R/W
        public int CpuStatus
        {
            get
            {
                Int32 CStatus = new Int32();
                Int32 SStatus = new Int32();
                Int32 CCount  = new Int32();

                if (Srv_GetStatus(Server, ref CStatus, ref SStatus, ref CCount) == 0)
                    return CStatus;
                else
                    return -1;
            }
            set
            {
                Srv_SetCpuStatus(Server, value);
            }
        }
        
        // Property Server Status Read Only
        public int ServerStatus
        {
            get
            {
                Int32 CStatus = new Int32();
                Int32 SStatus = new Int32();
                Int32 CCount = new Int32();
                if (Srv_GetStatus(Server, ref CStatus, ref SStatus, ref CCount) == 0)
                    return SStatus;
                else
                    return -1;
            }
        }
        
        // Property Clients Count Read Only
        public int ClientsCount
        {
            get
            {
                Int32 CStatus = new Int32();
                Int32 SStatus = new Int32();
                Int32 CCount = new Int32();
                if (Srv_GetStatus(Server, ref CStatus, ref SStatus, ref CCount) == 0)
                    return CCount;
                else
                    return -1;
            }
        }
               
        [DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
        protected static extern int Srv_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
        public string ErrorText(int Error)
        {
            StringBuilder Message = new StringBuilder(MsgTextLen);
            Srv_ErrorText(Error, Message, MsgTextLen);
            return Message.ToString();
        }
                
        #endregion
    }

    public class S7Partner
    {
        #region [Constants, private vars and TypeDefs]

        private const int MsgTextLen = 1024;
        
        // Status
        public static readonly int par_stopped    = 0;   // stopped
        public static readonly int par_connecting = 1;   // running and active connecting
        public static readonly int par_waiting    = 2;   // running and waiting for a connection
        public static readonly int par_linked     = 3;   // running and connected : linked
        public static readonly int par_sending    = 4;   // sending data
        public static readonly int par_receiving  = 5;   // receiving data
        public static readonly int par_binderror  = 6;   // error starting passive server

        // Errors
        public static readonly uint errParAddressInUse       = 0x00200000;
        public static readonly uint errParNoRoom             = 0x00300000;
        public static readonly uint errServerNoRoom          = 0x00400000;
        public static readonly uint errParInvalidParams      = 0x00500000;
        public static readonly uint errParNotLinked          = 0x00600000;
        public static readonly uint errParBusy               = 0x00700000;
        public static readonly uint errParFrameTimeout       = 0x00800000;
        public static readonly uint errParInvalidPDU         = 0x00900000;
        public static readonly uint errParSendTimeout        = 0x00A00000;
        public static readonly uint errParRecvTimeout        = 0x00B00000;
        public static readonly uint errParSendRefused        = 0x00C00000;
        public static readonly uint errParNegotiatingPDU     = 0x00D00000;
        public static readonly uint errParSendingBlock       = 0x00E00000;
        public static readonly uint errParRecvingBlock       = 0x00F00000;
        public static readonly uint errBindError             = 0x01000000;
        public static readonly uint errParDestroying         = 0x01100000;
        public static readonly uint errParInvalidParamNumber = 0x01200000; 
        public static readonly uint errParCannotChangeParam  = 0x01300000; 
     
        // Generic byte buffer structure, you may need to declare a more
        // specialistic one in your program.
        // It's used to cast the input pointer that cames from the callback.
        // See the passive partned demo and the delegate S7ParRecvCallback.

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S7Buffer
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x8000)]
            public byte[] Data;
        }

        // Job status
        private const int JobComplete = 0;
        private const int JobPending  = 1;

        private IntPtr Partner;

        private Int32 parBytesSent;
        private Int32 parBytesRecv;
	    private Int32 parSendErrors;
        private Int32 parRecvErrors;

        #endregion

        #region [Class Control]
        
        [DllImport(S7Consts.Snap7LibName)]
        protected static extern IntPtr Par_Create(Int32 ParActive);
        public S7Partner(int Active)
        {
            Partner= Par_Create(Active);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_Destroy(ref IntPtr Partner);
        ~S7Partner()
        {
            Par_Destroy(ref Partner);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_StartTo(
            IntPtr Partner,
            [MarshalAs(UnmanagedType.LPStr)] string LocalAddress,
            [MarshalAs(UnmanagedType.LPStr)] string RemoteAddress,
            UInt16 LocalTSAP,
            UInt16 RemoteTSAP);

        public int StartTo(
            string LocalAddress,
            string RemoteAddress,
            UInt16 LocalTSAP,
            UInt16 RemoteTSAP)
        {
            return Par_StartTo(Partner, LocalAddress, RemoteAddress, LocalTSAP, RemoteTSAP);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_Start(IntPtr Partner);
        public int Start()
        {
            return Par_Start(Partner);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_Stop(IntPtr Partner);
        public int Stop()
        {
            return Par_Stop(Partner);
        }

        // Get/SetParam needs a void* parameter, internally it decides the kind of pointer
        // in accord to ParamNumber.
        // To avoid the use of unsafe code we split the DLL functions and use overloaded methods.

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_i16(IntPtr Partner, Int32 ParamNumber, ref Int16 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Par_GetParam_i16(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_u16(IntPtr Partner, Int32 ParamNumber, ref UInt16 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Par_GetParam_u16(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_i32(IntPtr Partner, Int32 ParamNumber, ref Int32 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Par_GetParam_i32(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_u32(IntPtr Partner, Int32 ParamNumber, ref UInt32 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Par_GetParam_u32(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_i64(IntPtr Partner, Int32 ParamNumber, ref Int64 IntValue);
        public int GetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Par_GetParam_i64(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_GetParam")]
        protected static extern int Par_GetParam_u64(IntPtr Partner, Int32 ParamNumber, ref UInt64 IntValue);
        public int GetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Par_GetParam_u64(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_i16(IntPtr Partner, Int32 ParamNumber, ref Int16 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int16 IntValue)
        {
            return Par_SetParam_i16(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_u16(IntPtr Partner, Int32 ParamNumber, ref UInt16 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt16 IntValue)
        {
            return Par_SetParam_u16(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_i32(IntPtr Partner, Int32 ParamNumber, ref Int32 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int32 IntValue)
        {
            return Par_SetParam_i32(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_u32(IntPtr Partner, Int32 ParamNumber, ref UInt32 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt32 IntValue)
        {
            return Par_SetParam_u32(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_i64(IntPtr Partner, Int32 ParamNumber, ref Int64 IntValue);
        public int SetParam(Int32 ParamNumber, ref Int64 IntValue)
        {
            return Par_SetParam_i64(Partner, ParamNumber, ref IntValue);
        }

        [DllImport(S7Consts.Snap7LibName, EntryPoint = "Par_SetParam")]
        protected static extern int Par_SetParam_u64(IntPtr Partner, Int32 ParamNumber, ref UInt64 IntValue);
        public int SetParam(Int32 ParamNumber, ref UInt64 IntValue)
        {
            return Par_SetParam_u64(Partner, ParamNumber, ref IntValue);
        }

        #endregion

        #region [Data I/O functions : BSend]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_BSend(IntPtr Partner, UInt32 R_ID, byte[] Buffer, Int32 Size);
        public int BSend(UInt32 R_ID, byte[] Buffer, Int32 Size)
        {
            return Par_BSend(Partner, R_ID, Buffer, Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_AsBSend(IntPtr Partner, UInt32 R_ID, byte[] Buffer, Int32 Size);
        public int AsBSend(UInt32 R_ID, byte[] Buffer, Int32 Size)
        {
            return Par_AsBSend(Partner, R_ID, Buffer, Size);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_CheckAsBSendCompletion(IntPtr Partner, ref Int32 opResult);
        public bool CheckAsBSendCompletion(ref int opResult)
        {
            return Par_CheckAsBSendCompletion(Partner, ref opResult)==JobComplete;
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_WaitAsBSendCompletion(IntPtr Partner, Int32 Timeout);
        public int WaitAsBSendCompletion(int Timeout)
        {
            return Par_WaitAsBSendCompletion(Partner, Timeout);
        }

        public delegate void S7ParSendCompletion(IntPtr usrPtr, int opResult);

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_SetSendCallback(IntPtr Partner, S7ParSendCompletion Completion, IntPtr usrPtr);
        public int SetSendCallBack(S7ParSendCompletion Completion, IntPtr usrPtr)
        {
            return Par_SetSendCallback(Partner, Completion, usrPtr);
        }

        #endregion

        #region [Data I/O functions : BRecv]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_BRecv(IntPtr Partner, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size, UInt32 Timeout);
        public int BRecv(ref UInt32 R_ID, byte[] Buffer, ref Int32 Size, UInt32 Timeout)
        {
            return Par_BRecv(Partner, ref R_ID, Buffer, ref Size, Timeout);
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_CheckAsBRecvCompletion(IntPtr Partner, ref Int32 opResult, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size);
        public bool CheckAsBRecvCompletion(ref Int32 opResult, ref UInt32 R_ID, byte[] Buffer, ref Int32 Size)
        {
            Par_CheckAsBRecvCompletion(Partner, ref opResult, ref R_ID, Buffer, ref Size);
            return opResult == JobComplete;
        }

        public delegate void S7ParRecvCallback(IntPtr usrPtr, int opResult, uint R_ID, IntPtr pData, int Size);

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_SetRecvCallback(IntPtr Partner, S7ParRecvCallback Callback, IntPtr usrPtr);
        public int SetRecvCallback(S7ParRecvCallback Callback, IntPtr usrPtr)
        {
            return Par_SetRecvCallback(Partner, Callback, usrPtr);
        }

        #endregion

        #region [Info functions]

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_GetLastError(IntPtr Partner, ref Int32 LastError);
        public int LastError(ref Int32 LastError)
        {
            Int32 PartnerLastError = new Int32();
            if (Par_GetLastError(Partner, ref PartnerLastError) == 0)
                return (int)PartnerLastError;
            else
                return -1;
        }

        [DllImport(S7Consts.Snap7LibName, CharSet = CharSet.Ansi)]
        protected static extern int Par_ErrorText(int Error, StringBuilder ErrMsg, int TextSize);
        public string ErrorText(int Error)
        {
            StringBuilder Message = new StringBuilder(MsgTextLen);
            Par_ErrorText(Error, Message, MsgTextLen);
            return Message.ToString();
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_GetStats(IntPtr Partner, ref Int32 BytesSent, ref Int32 BytesRecv,
	       ref Int32 SendErrors, ref Int32 RecvErrors);
        
        private void GetStatistics()
        {
            if (Par_GetStats(Partner, ref parBytesSent, ref parBytesRecv, ref parSendErrors, ref parRecvErrors) != 0)
            {
                parBytesSent = -1;
                parBytesRecv = -1;
                parSendErrors = -1;
                parRecvErrors = -1;           
            }        
        }

        public int BytesSent
        {
            get
            {
                GetStatistics();
                return parBytesSent;
            }
        }

        public int BytesRecv
        {
            get
            {
                GetStatistics();
                return parBytesRecv;
            }
        }

        public int SendErrors
        {
            get
            {
                GetStatistics();
                return parSendErrors;
            }
        }

        public int RecvErrors
        {
            get
            {
                GetStatistics();
                return parRecvErrors;
            }
        }

        [DllImport(S7Consts.Snap7LibName)]
        protected static extern int Par_GetStatus(IntPtr Partner, ref Int32 Status);

        public int Status
        {
            get
            {
                int ParStatus = new int();
                if (Par_GetStatus(Partner, ref ParStatus) != 0)
                    return -1;
                else
                    return ParStatus;
            }             
        }
        // simply useful
        public bool Linked
        {
            get
            {
                return Status == par_linked;
            }
        }
        #endregion

    }
}
