From 9108dbde510075256937ca7b1aca99705ff2963b Mon Sep 17 00:00:00 2001 From: Michele Cattafesta Date: Mon, 2 May 2016 21:58:09 +0200 Subject: [PATCH] documentation, cleanup and unit tests. Signed-off-by: Michele Cattafesta --- S7.Net.Common/Conversion.cs | 17 +++ S7.Net.UnitTest/ConvertersUnitTest.cs | 28 +++++ S7.Net.UnitTest/S7.Net.UnitTest.csproj | 1 + S7.Net/PLC.cs | 160 ++++++++++++++++++------- 4 files changed, 161 insertions(+), 45 deletions(-) create mode 100644 S7.Net.UnitTest/ConvertersUnitTest.cs diff --git a/S7.Net.Common/Conversion.cs b/S7.Net.Common/Conversion.cs index 0b8b00d..360e282 100644 --- a/S7.Net.Common/Conversion.cs +++ b/S7.Net.Common/Conversion.cs @@ -5,6 +5,11 @@ namespace S7.Net { public static class Conversion { + /// + /// Converts a binary string to Int32 value + /// + /// + /// public static int BinStringToInt32(this string txt) { int cnt = 0; @@ -39,6 +44,11 @@ namespace S7.Net return null; } + /// + /// Converts the value to a binary string + /// + /// + /// public static string ValToBinString(this object value) { int cnt = 0; @@ -135,6 +145,13 @@ namespace S7.Net } } + /// + /// Helper to get a bit value given a byte and the bit index. + /// Example: DB1.DBX0.5 -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5); + /// + /// + /// + /// public static bool SelectBit(this byte data, int bitPosition) { int mask = 1 << bitPosition; diff --git a/S7.Net.UnitTest/ConvertersUnitTest.cs b/S7.Net.UnitTest/ConvertersUnitTest.cs new file mode 100644 index 0000000..abbe4ac --- /dev/null +++ b/S7.Net.UnitTest/ConvertersUnitTest.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using S7.Net; + +namespace S7.Net.UnitTest +{ + [TestClass] + public class ConvertersUnitTest + { + [TestMethod] + public void T00_TestSelectBit() + { + byte dummyByte = 5; // 0000 0101 + Assert.IsTrue(dummyByte.SelectBit(0)); + Assert.IsFalse(dummyByte.SelectBit(1)); + Assert.IsTrue(dummyByte.SelectBit(2)); + Assert.IsFalse(dummyByte.SelectBit(3)); + Assert.IsFalse(dummyByte.SelectBit(4)); + Assert.IsFalse(dummyByte.SelectBit(5)); + Assert.IsFalse(dummyByte.SelectBit(6)); + Assert.IsFalse(dummyByte.SelectBit(7)); + + } + } +} diff --git a/S7.Net.UnitTest/S7.Net.UnitTest.csproj b/S7.Net.UnitTest/S7.Net.UnitTest.csproj index e148f86..025cc39 100644 --- a/S7.Net.UnitTest/S7.Net.UnitTest.csproj +++ b/S7.Net.UnitTest/S7.Net.UnitTest.csproj @@ -52,6 +52,7 @@ + diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs index 27be3e2..f3037db 100644 --- a/S7.Net/PLC.cs +++ b/S7.Net/PLC.cs @@ -237,41 +237,6 @@ namespace S7.Net } } - private Types.ByteArray ReadDataRequestPackage(DataType dataType, int DB, int startByteAdr, int count = 1) - { - //single data req = 12 - var package = new Types.ByteArray(12); - package.Add(new byte[] {0x12, 0x0a, 0x10}); - switch (dataType) - { - case DataType.Timer: - case DataType.Counter: - package.Add((byte)dataType); - break; - default: - package.Add(0x02); - break; - } - - package.Add(Types.Word.ToByteArray((ushort)(count))); - package.Add(Types.Word.ToByteArray((ushort)(DB))); - package.Add((byte)dataType); - var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 - package.Add((byte)overflow); - switch (dataType) - { - case DataType.Timer: - case DataType.Counter: - package.Add(Types.Word.ToByteArray((ushort)(startByteAdr))); - break; - default: - package.Add(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); - break; - } - - return package; - } - /// /// Reads multiple vars in a single request. /// You have to create and pass a list of DataItems and you obtain in response the same list with the values. @@ -296,7 +261,7 @@ namespace S7.Net // package.Add(0x02); // datenart foreach (var dataItem in dataItems) { - package.Add(ReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count))); + package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count))); } _mSocket.Send(package.array, package.array.Length, SocketFlags.None); @@ -338,11 +303,11 @@ namespace S7.Net /// If the read was not successful, check LastErrorCode or LastErrorString. /// /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. - /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. /// Start byte address. If you want to read DB1.DBW200, this is 200. /// Byte count, if you want to read 120 bytes, set this to 120. This parameter can't be higher than 200. If you need more, use recursion. /// Returns the bytes in an array - public byte[] ReadBytes(DataType dataType, int DB, int startByteAdr, int count) + public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count) { byte[] bytes = new byte[count]; @@ -353,7 +318,7 @@ namespace S7.Net Types.ByteArray package = new ByteArray(packageSize); package.Add(ReadHeaderPackage()); // package.Add(0x02); // datenart - package.Add(ReadDataRequestPackage(dataType, DB, startByteAdr, count)); + package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count)); _mSocket.Send(package.array, package.array.Length, SocketFlags.None); @@ -380,6 +345,12 @@ namespace S7.Net } } + /// + /// Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read. + /// + /// + /// + /// public int VarTypeToByteLength(VarType varType, int varCount = 1) { switch (varType) @@ -404,6 +375,13 @@ namespace S7.Net } } + /// + /// Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format. + /// + /// + /// + /// + /// public object ParseBytes(VarType varType, byte[] bytes, int varCount) { if (bytes == null) return null; @@ -652,6 +630,16 @@ namespace S7.Net Types.Class.FromBytes(sourceClass, classType, resultBytes.ToArray()); } + + /// + /// Writes up to 200 bytes to the plc and returns NoError if successful. You must specify the memory area type, memory are address, byte start address and bytes count. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. + /// NoError if it was successful, or the error is specified public ErrorCode WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value) { byte[] bReceive = new byte[513]; @@ -701,7 +689,17 @@ namespace S7.Net } } - public object Write(DataType dataType, int db, int startByteAdr, object value) + /// + /// Takes in input an object and tries to parse it to an array of values. This can be used to write many data, all of the same type. + /// You must specify the memory area type, memory are address, byte start address and bytes count. + /// If the read was not successful, check LastErrorCode or LastErrorString. + /// + /// Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output. + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion. + /// NoError if it was successful, or the error is specified + public ErrorCode Write(DataType dataType, int db, int startByteAdr, object value) { byte[] package = null; @@ -752,7 +750,14 @@ namespace S7.Net return WriteBytes(dataType, db, startByteAdr, package); } - public object Write(string variable, object value) + /// + /// Writes a single variable from the plc, takes in input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// If the write was not successful, check LastErrorCode or LastErrorString. + /// + /// Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc. + /// Value to be written to the plc + /// NoError if it was successful, or the error is specified + public ErrorCode Write(string variable, object value) { DataType mDataType; int mDB; @@ -918,6 +923,13 @@ namespace S7.Net } } + /// + /// Writes a C# struct to a DB in the plc + /// + /// The struct to be written + /// Db address + /// Start bytes on the plc + /// NoError if it was successful, or the error is specified public ErrorCode WriteStruct(object structValue, int db, int startByteAdr = 0) { var bytes = Types.Struct.ToBytes(structValue).ToList(); @@ -925,6 +937,13 @@ namespace S7.Net return errCode; } + /// + /// Writes a C# class to a DB in the plc + /// + /// The class to be written + /// Db address + /// Start bytes on the plc + /// NoError if it was successful, or the error is specified public ErrorCode WriteClass(object classValue, int db, int startByteAdr = 0) { var bytes = Types.Class.ToBytes(classValue).ToList(); @@ -932,6 +951,13 @@ namespace S7.Net return errCode; } + /// + /// Writes multiple bytes to the plc. This uses recursion and it's not limited to 200 bytes. + /// + /// The bytes values to be written + /// Db address + /// Start bytes on the plc + /// NoError if it was successful, or the error is specified private ErrorCode WriteMultipleBytes(List bytes, int db, int startByteAdr = 0) { ErrorCode errCode = ErrorCode.NoError; @@ -962,10 +988,10 @@ namespace S7.Net /// /// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests. /// - /// - /// - /// - /// + /// Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc. + /// Start byte address. If you want to read DB1.DBW200, this is 200. + /// Byte count, if you want to read 120 bytes, set this to 120. This parameter can't be higher than 200. If you need more, use recursion. + /// Returns the bytes in a list private List ReadMultipleBytes(int numBytes, int db, int startByteAdr = 0) { List resultBytes = new List(); @@ -1005,6 +1031,50 @@ namespace S7.Net return package; } + /// + /// Create the bytes-package to request data from the plc. You have to specify the memory type (dataType), + /// the address of the memory, the address of the byte and the bytes count. + /// + /// MemoryType (DB, Timer, Counter, etc.) + /// Address of the memory to be read + /// Start address of the byte + /// Number of bytes to be read + /// + private Types.ByteArray CreateReadDataRequestPackage(DataType dataType, int db, int startByteAdr, int count = 1) + { + //single data req = 12 + var package = new Types.ByteArray(12); + package.Add(new byte[] { 0x12, 0x0a, 0x10 }); + switch (dataType) + { + case DataType.Timer: + case DataType.Counter: + package.Add((byte)dataType); + break; + default: + package.Add(0x02); + break; + } + + package.Add(Types.Word.ToByteArray((ushort)(count))); + package.Add(Types.Word.ToByteArray((ushort)(db))); + package.Add((byte)dataType); + var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191 + package.Add((byte)overflow); + switch (dataType) + { + case DataType.Timer: + case DataType.Counter: + package.Add(Types.Word.ToByteArray((ushort)(startByteAdr))); + break; + default: + package.Add(Types.Word.ToByteArray((ushort)((startByteAdr) * 8))); + break; + } + + return package; + } + #region IDisposable members