Merge pull request #285 from scamille/fb-var

Various binary data adjustments, small code refactoring
This commit is contained in:
Michael Croes
2020-09-03 20:18:57 +02:00
committed by GitHub
8 changed files with 240 additions and 207 deletions

View File

@@ -128,6 +128,26 @@ namespace S7.Net.UnitTest
Assert.AreEqual(val3, result3);
}
/// <summary>
/// Write/Read a large amount of data to test PDU max
/// </summary>
[TestMethod]
public async Task Test_Async_WriteLargeByteArray()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
var randomEngine = new Random();
var data = new byte[8192];
var db = 2;
randomEngine.NextBytes(data);
await plc.WriteBytesAsync(DataType.DataBlock, db, 0, data);
var readData = await plc.ReadBytesAsync(DataType.DataBlock, db, 0, data.Length);
CollectionAssert.AreEqual(data, readData);
}
/// <summary>
/// Read/Write a class that has the same properties of a DB with the same field in the same order
/// </summary>

View File

@@ -743,6 +743,26 @@ namespace S7.Net.UnitTest
Assert.AreEqual(tc.ShortVariable04.ShortVarialbe00, tc2.ShortVariable04.ShortVarialbe00);
}
/// <summary>
/// Write/Read a large amount of data to test PDU max
/// </summary>
[TestMethod]
public void T33_WriteLargeByteArray()
{
Assert.IsTrue(plc.IsConnected, "Before executing this test, the plc must be connected. Check constructor.");
var randomEngine = new Random();
var data = new byte[8192];
var db = 2;
randomEngine.NextBytes(data);
plc.WriteBytes(DataType.DataBlock, db, 0, data);
var readData = plc.ReadBytes(DataType.DataBlock, db, 0, data.Length);
CollectionAssert.AreEqual(data, readData);
}
[TestMethod, ExpectedException(typeof(PlcException))]
public void T18_ReadStructThrowsIfPlcIsNotConnected()
{

View File

@@ -1,7 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using System.Linq;
namespace S7.Net
{
@@ -27,17 +26,17 @@ namespace S7.Net
{
TPkt = tPKT;
var br = new BinaryReader(new MemoryStream(tPKT.Data));
HeaderLength = br.ReadByte();
HeaderLength = tPKT.Data[0]; // Header length excluding this length byte
if (HeaderLength >= 2)
{
PDUType = br.ReadByte();
PDUType = tPKT.Data[1];
if (PDUType == 0xf0) //DT Data
{
var flags = br.ReadByte();
var flags = tPKT.Data[2];
TPDUNumber = flags & 0x7F;
LastDataUnit = (flags & 0x80) > 0;
Data = br.ReadBytes(tPKT.Length - HeaderLength - 4); //4 = TPKT Size
Data = new byte[tPKT.Data.Length - HeaderLength - 1]; // substract header length byte + header length.
Array.Copy(tPKT.Data, HeaderLength + 1, Data, 0, Data.Length);
return;
}
//TODO: Handle other PDUTypes
@@ -105,20 +104,24 @@ namespace S7.Net
{
var segment = TPDU.Read(stream);
if (segment.LastDataUnit)
{
return segment.Data;
}
// More segments are expected, prepare a buffer to store all data
var buffer = new byte[segment.Data.Length];
var output = new MemoryStream(buffer);
output.Write(segment.Data, 0, segment.Data.Length);
Array.Copy(segment.Data, buffer, segment.Data.Length);
while (!segment.LastDataUnit)
{
segment = TPDU.Read(stream);
var previousLength = buffer.Length;
Array.Resize(ref buffer, buffer.Length + segment.Data.Length);
var lastPosition = output.Position;
output = new MemoryStream(buffer);
output.Write(segment.Data, (int) lastPosition, segment.Data.Length);
Array.Copy(segment.Data, 0, buffer, previousLength, segment.Data.Length);
}
return buffer.Take((int)output.Position).ToArray();
return buffer;
}
/// <summary>
@@ -131,19 +134,24 @@ namespace S7.Net
{
var segment = await TPDU.ReadAsync(stream);
if (segment.LastDataUnit)
{
return segment.Data;
}
// More segments are expected, prepare a buffer to store all data
var buffer = new byte[segment.Data.Length];
var output = new MemoryStream(buffer);
output.Write(segment.Data, 0, segment.Data.Length);
Array.Copy(segment.Data, buffer, segment.Data.Length);
while (!segment.LastDataUnit)
{
segment = await TPDU.ReadAsync(stream);
var previousLength = buffer.Length;
Array.Resize(ref buffer, buffer.Length + segment.Data.Length);
var lastPosition = output.Position;
output = new MemoryStream(buffer);
output.Write(segment.Data, (int) lastPosition, segment.Data.Length);
Array.Copy(segment.Data, 0, buffer, previousLength, segment.Data.Length);
}
return buffer.Take((int)output.Position).ToArray();
return buffer;
}
}
}

View File

@@ -0,0 +1,18 @@

namespace S7.Net.Helper
{
internal static class MemoryStreamExtension
{
/// <summary>
/// Helper function to write to whole content of the given byte array to a memory stream.
///
/// Writes all bytes in value from 0 to value.Length to the memory stream.
/// </summary>
/// <param name="stream"></param>
/// <param name="value"></param>
public static void WriteByteArray(this System.IO.MemoryStream stream, byte[] value)
{
stream.Write(value, 0, value.Length);
}
}
}

View File

@@ -1,4 +1,5 @@
using S7.Net.Types;
using S7.Net.Helper;
using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,21 +14,18 @@ namespace S7.Net
/// </summary>
/// <param name="amount"></param>
/// <returns></returns>
private ByteArray ReadHeaderPackage(int amount = 1)
private void BuildHeaderPackage(System.IO.MemoryStream stream, int amount = 1)
{
//header size = 19 bytes
var package = new Types.ByteArray(19);
package.Add(new byte[] { 0x03, 0x00 });
stream.WriteByteArray(new byte[] { 0x03, 0x00 });
//complete package size
package.Add(Types.Int.ToByteArray((short)(19 + (12 * amount))));
package.Add(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 });
stream.WriteByteArray(Types.Int.ToByteArray((short)(19 + (12 * amount))));
stream.WriteByteArray(new byte[] { 0x02, 0xf0, 0x80, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00 });
//data part size
package.Add(Types.Word.ToByteArray((ushort)(2 + (amount * 12))));
package.Add(new byte[] { 0x00, 0x00, 0x04 });
stream.WriteByteArray(Types.Word.ToByteArray((ushort)(2 + (amount * 12))));
stream.WriteByteArray(new byte[] { 0x00, 0x00, 0x04 });
//amount of requests
package.Add((byte)amount);
return package;
stream.WriteByte((byte)amount);
}
/// <summary>
@@ -39,39 +37,36 @@ namespace S7.Net
/// <param name="startByteAdr">Start address of the byte</param>
/// <param name="count">Number of bytes to be read</param>
/// <returns></returns>
private ByteArray CreateReadDataRequestPackage(DataType dataType, int db, int startByteAdr, int count = 1)
private void BuildReadDataRequestPackage(System.IO.MemoryStream stream, 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 });
stream.WriteByteArray(new byte[] { 0x12, 0x0a, 0x10 });
switch (dataType)
{
case DataType.Timer:
case DataType.Counter:
package.Add((byte)dataType);
stream.WriteByte((byte)dataType);
break;
default:
package.Add(0x02);
stream.WriteByte(0x02);
break;
}
package.Add(Word.ToByteArray((ushort)(count)));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
stream.WriteByteArray(Word.ToByteArray((ushort)(count)));
stream.WriteByteArray(Word.ToByteArray((ushort)(db)));
stream.WriteByte((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
stream.WriteByte((byte)overflow);
switch (dataType)
{
case DataType.Timer:
case DataType.Counter:
package.Add(Types.Word.ToByteArray((ushort)(startByteAdr)));
stream.WriteByteArray(Types.Word.ToByteArray((ushort)(startByteAdr)));
break;
default:
package.Add(Types.Word.ToByteArray((ushort)((startByteAdr) * 8)));
stream.WriteByteArray(Types.Word.ToByteArray((ushort)((startByteAdr) * 8)));
break;
}
return package;
}
/// <summary>

View File

@@ -39,11 +39,16 @@ namespace S7.Net
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null)
throw new WrongNumberOfBytesException("No data received in response to Communication Setup");
if (s7data.Length < 2)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
//Check for S7 Ack Data
if (s7data[1] != 0x03)
throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03);
if (s7data.Length < 20)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
}
@@ -67,20 +72,17 @@ namespace S7.Net
/// <returns>Returns the bytes in an array</returns>
public async Task<byte[]> ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count)
{
List<byte> resultBytes = new List<byte>();
int index = startByteAdr;
var resultBytes = new byte[count];
int index = 0;
while (count > 0)
{
//This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0.
var maxToRead = (int)Math.Min(count, MaxPDUSize - 18);
byte[] bytes = await ReadBytesWithSingleRequestAsync(dataType, db, index, maxToRead);
if (bytes == null)
return resultBytes.ToArray();
resultBytes.AddRange(bytes);
var maxToRead = Math.Min(count, MaxPDUSize - 18);
await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead);
count -= maxToRead;
index += maxToRead;
}
return resultBytes.ToArray();
return resultBytes;
}
/// <summary>
@@ -222,15 +224,16 @@ namespace S7.Net
{
// first create the header
int packageSize = 19 + (dataItems.Count * 12);
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage(dataItems.Count));
var package = new System.IO.MemoryStream(packageSize);
BuildHeaderPackage(package, dataItems.Count);
// package.Add(0x02); // datenart
foreach (var dataItem in dataItems)
{
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count));
}
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var dataToSend = package.ToArray();
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
var s7data = await COTP.TSDU.ReadAsync(stream); //TODO use Async
if (s7data == null || s7data[14] != 0xff)
@@ -264,11 +267,8 @@ namespace S7.Net
int count = value.Length;
while (count > 0)
{
//TODO: Figure out how to use MaxPDUSize here
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
var maxToWrite = (int)Math.Min(count, 200);
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
var maxToWrite = (int)Math.Min(count, MaxPDUSize - 35);
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite);
count -= maxToWrite;
localIndex += maxToWrite;
}
@@ -383,28 +383,24 @@ namespace S7.Net
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes);
}
private async Task<byte[]> ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, int count)
private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count)
{
var stream = GetStreamIfAvailable();
byte[] bytes = new byte[count];
// first create the header
int packageSize = 31;
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage());
int packageSize = 31;
var package = new System.IO.MemoryStream(packageSize);
BuildHeaderPackage(package);
// package.Add(0x02); // datenart
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count);
await stream.WriteAsync(package.Array, 0, package.Array.Length);
var dataToSend = package.ToArray();
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
AssertReadResponse(s7data, count);
for (int cnt = 0; cnt < count; cnt++)
bytes[cnt] = s7data[cnt + 18];
return bytes;
Array.Copy(s7data, 18, buffer, offset, count);
}
/// <summary>
@@ -435,40 +431,15 @@ namespace S7.Net
/// <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>A task that represents the asynchronous write operation.</returns>
private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value)
private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count)
{
var stream = GetStreamIfAvailable();
byte[] bReceive = new byte[513];
int varCount = 0;
try
{
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
var stream = GetStreamIfAvailable();
var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(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);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8)));
package.Add(new byte[] { 0, 4 });
package.Add(Word.ToByteArray((ushort)(varCount * 8)));
// now join the header and the data
package.Add(value);
await stream.WriteAsync(package.Array, 0, package.Array.Length);
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[14] != 0xff)
@@ -486,41 +457,17 @@ namespace S7.Net
{
var stream = GetStreamIfAvailable();
byte[] bReceive = new byte[513];
int varCount = 0;
try
{
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new Types.ByteArray(packageSize);
var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)(varCount)));
// now join the header and the data
package.Add(value);
await stream.WriteAsync(package.Array, 0, package.Array.Length);
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
var s7data = await COTP.TSDU.ReadAsync(stream);
if (s7data == null || s7data[14] != 0xff)
{
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
}
catch (Exception exc)
{

View File

@@ -1,9 +1,11 @@
using S7.Net.Types;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using S7.Net.Protocol;
using S7.Net.Helper;
//Implement synchronous methods here
namespace S7.Net
@@ -32,11 +34,16 @@ namespace S7.Net
var s7data = COTP.TSDU.Read(stream);
if (s7data == null)
throw new WrongNumberOfBytesException("No data received in response to Communication Setup");
if (s7data.Length < 2)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
//Check for S7 Ack Data
if (s7data[1] != 0x03)
throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03);
if (s7data.Length < 20)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
MaxPDUSize = (short)(s7data[18] * 256 + s7data[19]);
}
catch (Exception exc)
@@ -80,20 +87,17 @@ namespace S7.Net
/// <returns>Returns the bytes in an array</returns>
public byte[] ReadBytes(DataType dataType, int db, int startByteAdr, int count)
{
List<byte> resultBytes = new List<byte>();
int index = startByteAdr;
var result = new byte[count];
int index = 0;
while (count > 0)
{
//This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0.
var maxToRead = (int)Math.Min(count, MaxPDUSize - 18);
byte[] bytes = ReadBytesWithSingleRequest(dataType, db, index, maxToRead);
if (bytes == null)
return resultBytes.ToArray();
resultBytes.AddRange(bytes);
var maxToRead = Math.Min(count, MaxPDUSize - 18);
ReadBytesWithSingleRequest(dataType, db, startByteAdr + index, result, index, maxToRead);
count -= maxToRead;
index += maxToRead;
}
return resultBytes.ToArray();
return result;
}
/// <summary>
@@ -232,7 +236,7 @@ namespace S7.Net
//Snap7 seems to choke on PDU sizes above 256 even if snap7
//replies with bigger PDU size in connection setup.
var maxToWrite = Math.Min(count, MaxPDUSize - 28);//TODO tested only when the MaxPDUSize is 480
WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite);
count -= maxToWrite;
localIndex += maxToWrite;
}
@@ -339,28 +343,25 @@ namespace S7.Net
WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult();
}
private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count)
private void ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count)
{
var stream = GetStreamIfAvailable();
byte[] bytes = new byte[count];
try
{
// first create the header
int packageSize = 31;
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage());
int packageSize = 19 + 12; // 19 header + 12 for 1 request
var package = new System.IO.MemoryStream(packageSize);
BuildHeaderPackage(package);
// package.Add(0x02); // datenart
package.Add(CreateReadDataRequestPackage(dataType, db, startByteAdr, count));
BuildReadDataRequestPackage(package, dataType, db, startByteAdr, count);
stream.Write(package.Array, 0, package.Array.Length);
var dataToSend = package.ToArray();
stream.Write(dataToSend, 0, dataToSend.Length);
var s7data = COTP.TSDU.Read(stream);
AssertReadResponse(s7data, count);
for (int cnt = 0; cnt < count; cnt++)
bytes[cnt] = s7data[cnt + 18];
return bytes;
Array.Copy(s7data, 18, buffer, offset, count);
}
catch (Exception exc)
{
@@ -387,38 +388,14 @@ namespace S7.Net
S7WriteMultiple.ParseResponse(response, response.Length, dataItems);
}
private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count)
{
var stream = GetStreamIfAvailable();
int varCount = 0;
try
{
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
var stream = GetStreamIfAvailable();
var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count);
package.Add(new byte[] { 3, 0 });
//complete package size
package.Add(Int.ToByteArray((short)packageSize));
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(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);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8)));
package.Add(new byte[] { 0, 4 });
package.Add(Word.ToByteArray((ushort)(varCount * 8)));
// now join the header and the data
package.Add(value);
stream.Write(package.Array, 0, package.Array.Length);
stream.Write(dataToSend, 0, dataToSend.Length);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[14] != 0xff)
@@ -432,43 +409,85 @@ namespace S7.Net
}
}
private byte[] BuildWriteBytesPackage(DataType dataType, int db, int startByteAdr, byte[] value, int dataOffset, int count)
{
int varCount = count;
// first create the header
int packageSize = 35 + varCount;
var package = new MemoryStream(new byte[packageSize]);
package.WriteByte(3);
package.WriteByte(0);
//complete package size
package.WriteByteArray(Int.ToByteArray((short)packageSize));
package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1)));
package.WriteByteArray(new byte[] { 0, 0x0e });
package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4)));
package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x02 });
package.WriteByteArray(Word.ToByteArray((ushort)varCount));
package.WriteByteArray(Word.ToByteArray((ushort)(db)));
package.WriteByte((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.WriteByte((byte)overflow);
package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8)));
package.WriteByteArray(new byte[] { 0, 4 });
package.WriteByteArray(Word.ToByteArray((ushort)(varCount * 8)));
// now join the header and the data
package.Write(value, dataOffset, count);
return package.ToArray();
}
private byte[] BuildWriteBitPackage(DataType dataType, int db, int startByteAdr, bool bitValue, int bitAdr)
{
var stream = GetStreamIfAvailable();
var value = new[] { bitValue ? (byte)1 : (byte)0 };
int varCount = 1;
// first create the header
int packageSize = 35 + varCount;
var package = new MemoryStream(new byte[packageSize]);
package.WriteByte(3);
package.WriteByte(0);
//complete package size
package.WriteByteArray(Int.ToByteArray((short)packageSize));
package.WriteByteArray(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.WriteByteArray(Word.ToByteArray((ushort)(varCount - 1)));
package.WriteByteArray(new byte[] { 0, 0x0e });
package.WriteByteArray(Word.ToByteArray((ushort)(varCount + 4)));
package.WriteByteArray(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
package.WriteByteArray(Word.ToByteArray((ushort)varCount));
package.WriteByteArray(Word.ToByteArray((ushort)(db)));
package.WriteByte((byte)dataType);
var overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.WriteByte((byte)overflow);
package.WriteByteArray(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
package.WriteByteArray(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
package.WriteByteArray(Word.ToByteArray((ushort)(varCount)));
// now join the header and the data
package.WriteByteArray(value);
return package.ToArray();
}
private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
{
var stream = GetStreamIfAvailable();
int varCount = 0;
try
{
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
varCount = value.Length;
// first create the header
int packageSize = 35 + value.Length;
ByteArray package = new ByteArray(packageSize);
var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr);
package.Add(new byte[] { 3, 0, 0 });
package.Add((byte)packageSize);
package.Add(new byte[] { 2, 0xf0, 0x80, 0x32, 1, 0, 0 });
package.Add(Word.ToByteArray((ushort)(varCount - 1)));
package.Add(new byte[] { 0, 0x0e });
package.Add(Word.ToByteArray((ushort)(varCount + 4)));
package.Add(new byte[] { 0x05, 0x01, 0x12, 0x0a, 0x10, 0x01 }); //ending 0x01 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)varCount));
package.Add(Word.ToByteArray((ushort)(db)));
package.Add((byte)dataType);
int overflow = (int)(startByteAdr * 8 / 0xffffU); // handles words with address bigger than 8191
package.Add((byte)overflow);
package.Add(Word.ToByteArray((ushort)(startByteAdr * 8 + bitAdr)));
package.Add(new byte[] { 0, 0x03 }); //ending 0x03 is used for writing a sinlge bit
package.Add(Word.ToByteArray((ushort)(varCount)));
// now join the header and the data
package.Add(value);
stream.Write(package.Array, 0, package.Array.Length);
stream.Write(dataToSend, 0, dataToSend.Length);
var s7data = COTP.TSDU.Read(stream);
if (s7data == null || s7data[14] != 0xff)
{
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
}
}
catch (Exception exc)
{
@@ -496,15 +515,16 @@ namespace S7.Net
{
// first create the header
int packageSize = 19 + (dataItems.Count * 12);
ByteArray package = new ByteArray(packageSize);
package.Add(ReadHeaderPackage(dataItems.Count));
var package = new System.IO.MemoryStream(packageSize);
BuildHeaderPackage(package, dataItems.Count);
// package.Add(0x02); // datenart
foreach (var dataItem in dataItems)
{
package.Add(CreateReadDataRequestPackage(dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count)));
BuildReadDataRequestPackage(package, dataItem.DataType, dataItem.DB, dataItem.StartByteAdr, VarTypeToByteLength(dataItem.VarType, dataItem.Count));
}
stream.Write(package.Array, 0, package.Array.Length);
var dataToSend = package.ToArray();
stream.Write(dataToSend, 0, dataToSend.Length);
var s7data = COTP.TSDU.Read(stream); //TODO use Async
if (s7data == null || s7data[14] != 0xff)

View File

@@ -50,6 +50,11 @@ namespace S7.Net.Types
list.AddRange(items);
}
public void Add(IEnumerable<byte> items)
{
list.AddRange(items);
}
public void Add(ByteArray byteArray)
{
list.AddRange(byteArray.Array);