From 73437f9ea3f16cb5031f65d9d2f4015ae89afc45 Mon Sep 17 00:00:00 2001 From: Michele Cattafesta Date: Mon, 22 Dec 2014 14:31:52 +0100 Subject: [PATCH] Added Unit Tests. Signed-off-by: Michele Cattafesta --- S7.Net.UnitTest/Helpers/ConsoleManager.cs | 91 + S7.Net.UnitTest/Helpers/NativeMethods.cs | 51 + S7.Net.UnitTest/Helpers/S7TestServer.cs | 89 + S7.Net.UnitTest/Helpers/TestClass.cs | 55 + S7.Net.UnitTest/Helpers/TestStruct.cs | 55 + S7.Net.UnitTest/Properties/AssemblyInfo.cs | 36 + S7.Net.UnitTest/S7.Net.UnitTest.csproj | 102 + S7.Net.UnitTest/S7NetTests.cs | 207 ++ S7.Net.UnitTest/Snap7/snap7.net.cs | 2018 ++++++++++++++++++++ S7.Net.UnitTest/snap7.dll | Bin 0 -> 209920 bytes S7.sln | 6 + 11 files changed, 2710 insertions(+) create mode 100644 S7.Net.UnitTest/Helpers/ConsoleManager.cs create mode 100644 S7.Net.UnitTest/Helpers/NativeMethods.cs create mode 100644 S7.Net.UnitTest/Helpers/S7TestServer.cs create mode 100644 S7.Net.UnitTest/Helpers/TestClass.cs create mode 100644 S7.Net.UnitTest/Helpers/TestStruct.cs create mode 100644 S7.Net.UnitTest/Properties/AssemblyInfo.cs create mode 100644 S7.Net.UnitTest/S7.Net.UnitTest.csproj create mode 100644 S7.Net.UnitTest/S7NetTests.cs create mode 100644 S7.Net.UnitTest/Snap7/snap7.net.cs create mode 100644 S7.Net.UnitTest/snap7.dll diff --git a/S7.Net.UnitTest/Helpers/ConsoleManager.cs b/S7.Net.UnitTest/Helpers/ConsoleManager.cs new file mode 100644 index 0000000..f110b92 --- /dev/null +++ b/S7.Net.UnitTest/Helpers/ConsoleManager.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Security; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.IO; + +namespace S7.Net.UnitTest.Helpers +{ + [SuppressUnmanagedCodeSecurity] + public static class ConsoleManager + { + public static bool HasConsole + { + get { return NativeMethods.GetConsoleWindow() != IntPtr.Zero; } + } + + /// + /// Creates a new console instance if the process is not attached to a console already. + /// + public static void Show() + { + //#if DEBUG + if (!HasConsole) + { + NativeMethods.AllocConsole(); + InvalidateOutAndError(); + } + //#endif + } + + /// + /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown. + /// + public static void Hide() + { + //#if DEBUG + if (HasConsole) + { + SetOutAndErrorNull(); + NativeMethods.FreeConsole(); + } + //#endif + } + + public static void Toggle() + { + if (HasConsole) + { + Hide(); + } + else + { + Show(); + } + } + + static void InvalidateOutAndError() + { + Type type = typeof(System.Console); + + System.Reflection.FieldInfo _out = type.GetField("_out", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + System.Reflection.FieldInfo _error = type.GetField("_error", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + + System.Diagnostics.Debug.Assert(_out != null); + System.Diagnostics.Debug.Assert(_error != null); + + System.Diagnostics.Debug.Assert(_InitializeStdOutError != null); + + _out.SetValue(null, null); + _error.SetValue(null, null); + + _InitializeStdOutError.Invoke(null, new object[] { true }); + } + + static void SetOutAndErrorNull() + { + Console.SetOut(TextWriter.Null); + Console.SetError(TextWriter.Null); + } + } +} + diff --git a/S7.Net.UnitTest/Helpers/NativeMethods.cs b/S7.Net.UnitTest/Helpers/NativeMethods.cs new file mode 100644 index 0000000..774136e --- /dev/null +++ b/S7.Net.UnitTest/Helpers/NativeMethods.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace S7.Net.UnitTest.Helpers +{ + + internal static class NativeMethods + { + [StructLayout(LayoutKind.Sequential)] + public struct CURSORINFO + { + public Int32 cbSize; + public Int32 flags; + public IntPtr hCursor; + public POINTAPI ptScreenPos; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINTAPI + { + public int x; + public int y; + } + + [DllImport("user32.dll")] + public static extern int BringWindowToTop(IntPtr hwnd); + + [DllImport("kernel32.dll")] + public static extern bool AllocConsole(); + + [DllImport("kernel32.dll")] + public static extern bool FreeConsole(); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetConsoleWindow(); + + [DllImport("kernel32.dll")] + public static extern int GetConsoleOutputCP(); + + [DllImport("user32.dll")] + public static extern bool GetCursorInfo(out CURSORINFO pci); + + [DllImport("user32.dll")] + public static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); + } + +} diff --git a/S7.Net.UnitTest/Helpers/S7TestServer.cs b/S7.Net.UnitTest/Helpers/S7TestServer.cs new file mode 100644 index 0000000..4a94142 --- /dev/null +++ b/S7.Net.UnitTest/Helpers/S7TestServer.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Snap7; + +namespace S7.Net.UnitTest.Helpers +{ + class S7TestServer + { + static S7Server Server; + static private byte[] DB1 = new byte[512]; // Our DB1 + static private byte[] DB2 = new byte[1028]; // Our DB2 + static private byte[] DB3 = new byte[1024]; // Our DB3 + private static S7Server.TSrvCallback TheEventCallBack; // <== Static var containig the callback + private static S7Server.TSrvCallback TheReadCallBack; // <== Static var containig the callback + + // 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. + + static void EventCallback(IntPtr usrPtr, ref S7Server.USrvEvent Event, int Size) + { + Console.WriteLine(Server.EventText(ref Event)); + } + + static void ReadEventCallback(IntPtr usrPtr, ref S7Server.USrvEvent Event, int Size) + { + Console.WriteLine(Server.EventText(ref Event)); + } + + public static void Start() + { + Server = new S7Server(); + // Share some resources with our virtual PLC + Server.RegisterArea(S7Server.srvAreaDB, // We are registering a DB + 1, // Its number is 1 (DB1) + DB1, // Our buffer for DB1 + DB1.Length); // Its size + // Do the same for DB2 and DB3 + Server.RegisterArea(S7Server.srvAreaDB, 2, DB2, DB2.Length); + Server.RegisterArea(S7Server.srvAreaDB, 3, DB3, DB3.Length); + + // Exclude read event to avoid the double report + // Set the callbacks (using the static var to avoid the garbage collect) + TheEventCallBack = new S7Server.TSrvCallback(EventCallback); + TheReadCallBack = new S7Server.TSrvCallback(ReadEventCallback); + + Server.EventMask = ~S7Server.evcDataRead; + Server.SetEventsCallBack(TheEventCallBack, IntPtr.Zero); + Server.SetReadEventsCallBack(TheReadCallBack, IntPtr.Zero); + + // Uncomment next line if you don't want to see wrapped messages + // (Note : Doesn't work in Mono 2.10) + + // Console.SetBufferSize(100, Int16.MaxValue - 1); + + // 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"); + int Error = Server.Start(); + //if (Error == 0) + //{ + // // Now the server is running ... wait a key to terminate + // //Console.ReadKey(); + // Server.Stop(); + //} + //else + // Console.WriteLine(Server.ErrorText(Error)); + // 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". + } + + public static void Stop() + { + int Error = Server.Stop(); + } + } +} diff --git a/S7.Net.UnitTest/Helpers/TestClass.cs b/S7.Net.UnitTest/Helpers/TestClass.cs new file mode 100644 index 0000000..bb23785 --- /dev/null +++ b/S7.Net.UnitTest/Helpers/TestClass.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace S7.Net.UnitTest.Helpers +{ + class TestClass + { + /// + /// DB1.DBX0.0 + /// + public bool BitVariable00 { get; set; } + public bool BitVariable01 { get; set; } + public bool BitVariable02 { get; set; } + public bool BitVariable03 { get; set; } + public bool BitVariable04 { get; set; } + public bool BitVariable05 { get; set; } + public bool BitVariable06 { get; set; } + public bool BitVariable07 { get; set; } + + /// + /// DB1.DBX1.0 + /// + public bool BitVariable10 { get; set; } + public bool BitVariable11 { get; set; } + public bool BitVariable12 { get; set; } + public bool BitVariable13 { get; set; } + public bool BitVariable14 { get; set; } + public bool BitVariable15 { get; set; } + public bool BitVariable16 { get; set; } + public bool BitVariable17 { get; set; } + + /// + /// DB1.DBW2 + /// + public short IntVariable { get; set; } + + /// + /// DB1.DBD4 + /// + public double RealVariable { get; set; } + + /// + /// DB1.DBD8 + /// + public int DIntVariable { get; set; } + + /// + /// DB1.DBD12 + /// + public ushort DWordVariable { get; set; } + } +} diff --git a/S7.Net.UnitTest/Helpers/TestStruct.cs b/S7.Net.UnitTest/Helpers/TestStruct.cs new file mode 100644 index 0000000..448e9ce --- /dev/null +++ b/S7.Net.UnitTest/Helpers/TestStruct.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace S7.Net.UnitTest.Helpers +{ + public struct TestStruct + { + /// + /// DB1.DBX0.0 + /// + public bool BitVariable00; + public bool BitVariable01; + public bool BitVariable02; + public bool BitVariable03; + public bool BitVariable04; + public bool BitVariable05; + public bool BitVariable06; + public bool BitVariable07; + + /// + /// DB1.DBX1.0 + /// + public bool BitVariable10; + public bool BitVariable11; + public bool BitVariable12; + public bool BitVariable13; + public bool BitVariable14; + public bool BitVariable15; + public bool BitVariable16; + public bool BitVariable17; + + /// + /// DB1.DBW2 + /// + public short IntVariable; + + /// + /// DB1.DBD4 + /// + public double RealVariable; + + /// + /// DB1.DBD8 + /// + public int DIntVariable; + + /// + /// DB1.DBD12 + /// + public ushort DWordVariable; + } +} diff --git a/S7.Net.UnitTest/Properties/AssemblyInfo.cs b/S7.Net.UnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..64fefd7 --- /dev/null +++ b/S7.Net.UnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("S7.Net.UnitTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("S7Net.UnitTest")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6f73e1b1-301b-471e-9f38-3dcbddbcfc21")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj new file mode 100644 index 0000000..9b825a2 --- /dev/null +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -0,0 +1,102 @@ + + + + Debug + AnyCPU + {303CCED6-9ABC-4899-A509-743341AAA804} + Library + Properties + S7.UnitTest + S7Net.UnitTest + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + {bfd484f9-3f04-42a2-bf2a-60a189a25dcf} + S7.Net + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/S7.Net.UnitTest/S7NetTests.cs b/S7.Net.UnitTest/S7NetTests.cs new file mode 100644 index 0000000..c1f7bd5 --- /dev/null +++ b/S7.Net.UnitTest/S7NetTests.cs @@ -0,0 +1,207 @@ +#region Using +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using S7.Net; +using S7.Net.UnitTest.Helpers; +using S7.Net.UnitTest; +using System.ServiceProcess; + +#endregion + +/** + * About the tests: + * --------------------------------------------------------------------------- + * The tests were written to show how to use this library to read and write + * different types of values, how to box and unbox values and of course to + * address some of the bugs of the library. + * These tests are not meant to cover 100% the code, but to check that once a + * variable is written, it stores the correct value. + * ---------------------------------------------------------------------------- + * The plc used for the tests is the S7 "server" provided by Snap7 opensource + * library, that you can get for free here:http://snap7.sourceforge.net/ + * The implementation of the server will not be discussed here, but there are + * some issues with the interop that cause the server, and unit test, to fail + * under some circumstances, like "click on Run all tests" too much. + * This doesn't mean that S7.Net has bugs, but that the implementation of the + * server has problems. + * + */ +namespace S7.Net.UnitTest +{ + [TestClass] + public class S7NetTests + { + #region Constants + const int DB2 = 2; + #endregion + + #region Private fields + Plc plc; + #endregion + + #region Constructor + /// + /// Create a plc that will connect to localhost (Snap 7 server) and connect to it + /// + public S7NetTests() + { + plc = new Plc(CpuType.S7300, "127.0.0.1", 0, 2); + //ConsoleManager.Show(); + ShutDownServiceS7oiehsx64(); + S7TestServer.Start(); + plc.Open(); + } + + #endregion + + #region Tests + + [TestMethod] + public void T00_TestConnection() + { + if (plc.IsConnected == false) + { + var error = plc.Open(); + Assert.AreEqual(ErrorCode.NoError, error, "If you have s7 installed you must close s7oiehsx64 service."); + } + } + + /// + /// Read/Write a single Int16 or UInt16 with a single request. + /// Test that writing a UInt16 (ushort) and reading it gives the correct value. + /// Test also that writing a Int16 (short) and reading it gives the correct value. + /// + [TestMethod] + public void T01_WriteAndReadInt16Variable() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + // To write a ushort i don't need any cast, only unboxing must be done + ushort val = 40000; + plc.Write("DB1.DBW0", val); + ushort result = (ushort)plc.Read("DB1.DBW0"); + Assert.AreEqual(val, result, "A ushort goes from 0 to 64512"); + + // To write a short i need to convert it to UShort, then i need to reconvert the readed value to get + // the negative sign back + // Depending if i'm writing on a DWORD or on a DEC, i will see ushort or short value in the plc + short value = -100; + Assert.IsTrue(plc.IsConnected, "After connecting, IsConnected must be set to true"); + plc.Write("DB1.DBW0", value.ConvertToUshort()); + short result2 = ((ushort)plc.Read("DB1.DBW0")).ConvertToShort(); + Assert.AreEqual(value, result2, "A short goes from -32767 to 32766"); + } + + /// + /// Read/Write a single Int32 or UInt32 with a single request. + /// Test that writing a UInt32 (uint) and reading it gives the correct value. + /// Test also that writing a Int32 (int) and reading it gives the correct value. + /// + [TestMethod] + public void T02_WriteAndReadInt32Variable() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + // To write a uint I don't need any cast, only unboxing must be done + uint val = 1000; + plc.Write("DB1.DBD40", val); + uint result = (uint)plc.Read("DB1.DBD40"); + Assert.AreEqual(val, result); + + // To write a int I need to convert it to uint, then I need to reconvert the readed value to get + // the negative sign back + // Depending if I'm writing on a DBD or on a LONG, I will see uint or int value in the plc + int value = -60000; + plc.Write("DB1.DBD60", value); + int result2 = ((uint)plc.Read("DB1.DBD60")).ConvertToInt(); + Assert.AreEqual(value, result2); + } + + /// + /// Read/Write a single REAL with a single request. + /// Test that writing a double and reading it gives the correct value. + /// + [TestMethod] + public void T03_WriteAndReadRealVariables() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + // Reading and writing a double is quite complicated, because it needs to be converted to DWord before the write, + // then reconvert to double after the read. + double val = 35.687; + plc.Write("DB1.DBD40", val.ConvertToUInt()); + double result = ((uint)plc.Read("DB1.DBD40")).ConvertToDouble(); + Assert.AreEqual(val, Math.Round(result, 3)); // float lose precision, so i need to round it + } + + /// + /// Read/Write a class that has the same properties of a DB with the same field in the same order + /// + [TestMethod] + public void T04_ReadAndWriteClass() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + TestClass tc = new TestClass(); + tc.BitVariable00 = true; + tc.BitVariable10 = true; + tc.DIntVariable = -100000; + tc.IntVariable = -15000; + tc.RealVariable = -154.789; + tc.DWordVariable = 850; + plc.WriteClass(tc, DB2); + TestClass tc2 = new TestClass(); + // Values that are read from a class are stored inside the class itself, that is passed by reference + plc.ReadClass(tc2, DB2); + Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00); + Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10); + Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable); + Assert.AreEqual(tc.IntVariable, tc2.IntVariable); + Assert.AreEqual(tc.RealVariable, Math.Round(tc2.RealVariable, 3)); + Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable); + } + + /// + /// Read/Write a struct that has the same properties of a DB with the same field in the same order + /// + [TestMethod] + public void T05_ReadAndWriteStruct() + { + Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor."); + + TestStruct tc = new TestStruct(); + tc.BitVariable00 = true; + tc.BitVariable10 = true; + tc.DIntVariable = -100000; + tc.IntVariable = -15000; + tc.RealVariable = -154.789; + tc.DWordVariable = 850; + plc.WriteStruct(tc, DB2); + // Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct + TestStruct tc2 = (TestStruct)plc.ReadStruct(typeof(TestStruct), DB2); + Assert.AreEqual(tc.BitVariable00, tc2.BitVariable00); + Assert.AreEqual(tc.BitVariable10, tc2.BitVariable10); + Assert.AreEqual(tc.DIntVariable, tc2.DIntVariable); + Assert.AreEqual(tc.IntVariable, tc2.IntVariable); + Assert.AreEqual(tc.RealVariable, Math.Round(tc2.RealVariable, 3)); + Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable); + } + + #endregion + + #region Private methods + + private static void ShutDownServiceS7oiehsx64() + { + ServiceController sc = new ServiceController("s7oiehsx64"); + switch (sc.Status) + { + case ServiceControllerStatus.Running: + sc.Stop(); + break; + } + } + + #endregion + } +} diff --git a/S7.Net.UnitTest/Snap7/snap7.net.cs b/S7.Net.UnitTest/Snap7/snap7.net.cs new file mode 100644 index 0000000..98efa75 --- /dev/null +++ b/S7.Net.UnitTest/Snap7/snap7.net.cs @@ -0,0 +1,2018 @@ +/*=============================================================================| +| 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 + + } +} diff --git a/S7.Net.UnitTest/snap7.dll b/S7.Net.UnitTest/snap7.dll new file mode 100644 index 0000000000000000000000000000000000000000..d52c361ea4c2cf7581e1f755ee683558b8a632b3 GIT binary patch literal 209920 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�zh5$A_bXPPO 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 z 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�Cn*!| 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 ON5DtGN8tYgfqw&>FvYh3 literal 0 HcmV?d00001 diff --git a/S7.sln b/S7.sln index 0cd6962..d1dc768 100644 --- a/S7.sln +++ b/S7.sln @@ -8,6 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "S7.Net.UnitTest", "S7.Net.UnitTest\S7.Net.UnitTest.csproj", "{303CCED6-9ABC-4899-A509-743341AAA804}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,6 +20,10 @@ Global {BFD484F9-3F04-42A2-BF2A-60A189A25DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFD484F9-3F04-42A2-BF2A-60A189A25DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFD484F9-3F04-42A2-BF2A-60A189A25DCF}.Release|Any CPU.Build.0 = Release|Any CPU + {303CCED6-9ABC-4899-A509-743341AAA804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {303CCED6-9ABC-4899-A509-743341AAA804}.Debug|Any CPU.Build.0 = Debug|Any CPU + {303CCED6-9ABC-4899-A509-743341AAA804}.Release|Any CPU.ActiveCfg = Release|Any CPU + {303CCED6-9ABC-4899-A509-743341AAA804}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE