documentation, cleanup and unit tests.

Signed-off-by: Michele Cattafesta <michele.cattafesta@mesta-automation.com>
This commit is contained in:
Michele Cattafesta
2016-05-02 21:58:09 +02:00
parent 28c71c444d
commit 9108dbde51
4 changed files with 161 additions and 45 deletions

View File

@@ -5,6 +5,11 @@ namespace S7.Net
{
public static class Conversion
{
/// <summary>
/// Converts a binary string to Int32 value
/// </summary>
/// <param name="txt"></param>
/// <returns></returns>
public static int BinStringToInt32(this string txt)
{
int cnt = 0;
@@ -39,6 +44,11 @@ namespace S7.Net
return null;
}
/// <summary>
/// Converts the value to a binary string
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ValToBinString(this object value)
{
int cnt = 0;
@@ -135,6 +145,13 @@ namespace S7.Net
}
}
/// <summary>
/// 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);
/// </summary>
/// <param name="data"></param>
/// <param name="bitPosition"></param>
/// <returns></returns>
public static bool SelectBit(this byte data, int bitPosition)
{
int mask = 1 << bitPosition;

View File

@@ -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));
}
}
}

View File

@@ -52,6 +52,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="ConvertersUnitTest.cs" />
<Compile Include="Helpers\ConsoleManager.cs" />
<Compile Include="Helpers\NativeMethods.cs" />
<Compile Include="Helpers\S7TestServer.cs" />

View File

@@ -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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="DB">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.</param>
/// <param name="db">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.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="count">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.</param>
/// <returns>Returns the bytes in an array</returns>
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
}
}
/// <summary>
/// Given a S7 variable type (Bool, Word, DWord, etc.), it returns how many bytes to read.
/// </summary>
/// <param name="varType"></param>
/// <param name="varCount"></param>
/// <returns></returns>
public int VarTypeToByteLength(VarType varType, int varCount = 1)
{
switch (varType)
@@ -404,6 +375,13 @@ namespace S7.Net
}
}
/// <summary>
/// Given a S7 variable type (Bool, Word, DWord, etc.), it converts the bytes in the appropriate C# format.
/// </summary>
/// <param name="varType"></param>
/// <param name="bytes"></param>
/// <param name="varCount"></param>
/// <returns></returns>
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());
}
/// <summary>
/// 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.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">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.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
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)
/// <summary>
/// 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.
/// </summary>
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
/// <param name="db">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.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
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)
/// <summary>
/// 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.
/// </summary>
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
/// <param name="value">Value to be written to the plc</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
public ErrorCode Write(string variable, object value)
{
DataType mDataType;
int mDB;
@@ -918,6 +923,13 @@ namespace S7.Net
}
}
/// <summary>
/// Writes a C# struct to a DB in the plc
/// </summary>
/// <param name="structValue">The struct to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the plc</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
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;
}
/// <summary>
/// Writes a C# class to a DB in the plc
/// </summary>
/// <param name="classValue">The class to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the plc</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
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;
}
/// <summary>
/// Writes multiple bytes to the plc. This uses recursion and it's not limited to 200 bytes.
/// </summary>
/// <param name="bytes">The bytes values to be written</param>
/// <param name="db">Db address</param>
/// <param name="startByteAdr">Start bytes on the plc</param>
/// <returns>NoError if it was successful, or the error is specified</returns>
private ErrorCode WriteMultipleBytes(List<byte> bytes, int db, int startByteAdr = 0)
{
ErrorCode errCode = ErrorCode.NoError;
@@ -962,10 +988,10 @@ namespace S7.Net
/// <summary>
/// Reads a number of bytes from a DB starting from a specified index. This handles more than 200 bytes with multiple requests.
/// </summary>
/// <param name="numBytes"></param>
/// <param name="db"></param>
/// <param name="startByteAdr"></param>
/// <returns></returns>
/// <param name="db">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.</param>
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
/// <param name="numBytes">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.</param>
/// <returns>Returns the bytes in a list</returns>
private List<byte> ReadMultipleBytes(int numBytes, int db, int startByteAdr = 0)
{
List<byte> resultBytes = new List<byte>();
@@ -1005,6 +1031,50 @@ namespace S7.Net
return package;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="dataType">MemoryType (DB, Timer, Counter, etc.)</param>
/// <param name="db">Address of the memory to be read</param>
/// <param name="startByteAdr">Start address of the byte</param>
/// <param name="count">Number of bytes to be read</param>
/// <returns></returns>
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