mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-24 15:38:25 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ced10b4eca | ||
|
|
632e1c14ac | ||
|
|
aa50280233 | ||
|
|
3a794e8a46 | ||
|
|
0b8bd66bf7 | ||
|
|
e66d21af05 | ||
|
|
44ee651ac4 | ||
|
|
a1b4694ef6 | ||
|
|
d10c15b80f | ||
|
|
5225c8bffd | ||
|
|
aa03400350 | ||
|
|
2b4ec6d9dd | ||
|
|
54f3de6c9f | ||
|
|
e6d14587d3 | ||
|
|
e63d92c61c | ||
|
|
b4b94e1777 | ||
|
|
13c25fc20b | ||
|
|
82e29837a2 | ||
|
|
1afb07774b | ||
|
|
051091919f | ||
|
|
478c1aed52 | ||
|
|
924eb9c48f | ||
|
|
eb8e188c86 | ||
|
|
37384d2a92 | ||
|
|
fdd4519f64 | ||
|
|
52c60f6eaf | ||
|
|
926d74f1d2 | ||
|
|
9b89acfb91 | ||
|
|
de0a9e64dc | ||
|
|
dfcc4c7408 |
@@ -1,4 +1,5 @@
|
||||
|
||||
using S7.Net.Types;
|
||||
|
||||
namespace S7.Net.UnitTest.Helpers
|
||||
{
|
||||
public struct TestStruct
|
||||
@@ -7,6 +8,7 @@ namespace S7.Net.UnitTest.Helpers
|
||||
/// DB1.DBX0.0
|
||||
/// </summary>
|
||||
public bool BitVariable00;
|
||||
|
||||
public bool BitVariable01;
|
||||
public bool BitVariable02;
|
||||
public bool BitVariable03;
|
||||
@@ -19,6 +21,7 @@ namespace S7.Net.UnitTest.Helpers
|
||||
/// DB1.DBX1.0
|
||||
/// </summary>
|
||||
public bool BitVariable10;
|
||||
|
||||
public bool BitVariable11;
|
||||
public bool BitVariable12;
|
||||
public bool BitVariable13;
|
||||
@@ -51,5 +54,17 @@ namespace S7.Net.UnitTest.Helpers
|
||||
/// DB1.DBD16
|
||||
/// </summary>
|
||||
public ushort DWordVariable;
|
||||
|
||||
/// <summary>
|
||||
/// DB1.DBX20.0
|
||||
/// </summary>
|
||||
[S7String(S7StringType.S7WString, 10)]
|
||||
public string WStringVariable;
|
||||
|
||||
/// <summary>
|
||||
/// DB1.DBX44.0
|
||||
/// </summary>
|
||||
[S7String(S7StringType.S7String, 10)]
|
||||
public string StringVariable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,9 @@ namespace S7.Net.UnitTest
|
||||
IntVariable = -15000,
|
||||
LRealVariable = -154.789,
|
||||
RealVariable = -154.789f,
|
||||
DWordVariable = 850
|
||||
DWordVariable = 850,
|
||||
WStringVariable = "ÄÜÉÊéà",
|
||||
StringVariable = "Hallo"
|
||||
};
|
||||
plc.WriteStruct(tc, DB2);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
@@ -223,6 +225,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
|
||||
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
|
||||
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
|
||||
Assert.AreEqual(tc.WStringVariable, tc2.WStringVariable);
|
||||
Assert.AreEqual(tc.StringVariable, tc2.StringVariable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -739,7 +743,9 @@ namespace S7.Net.UnitTest
|
||||
IntVariable = -15000,
|
||||
LRealVariable = -154.789,
|
||||
RealVariable = -154.789f,
|
||||
DWordVariable = 850
|
||||
DWordVariable = 850,
|
||||
WStringVariable = "ÄÜÉÊéà",
|
||||
StringVariable = "Hallo"
|
||||
};
|
||||
|
||||
plc.WriteStruct(ts, DB2);
|
||||
@@ -756,6 +762,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable);
|
||||
Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable);
|
||||
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
|
||||
Assert.AreEqual(ts2.WStringVariable, ts2Generic.WStringVariable);
|
||||
Assert.AreEqual(ts2.StringVariable, ts2Generic.StringVariable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -212,6 +212,9 @@ namespace S7.Net.UnitTest
|
||||
tc.LRealVariable = -154.789;
|
||||
tc.RealVariable = -154.789f;
|
||||
tc.DWordVariable = 850;
|
||||
tc.WStringVariable = "ÄÜÉÊéà";
|
||||
tc.StringVariable = "Hallo";
|
||||
|
||||
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);
|
||||
@@ -222,6 +225,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(tc.LRealVariable, tc2.LRealVariable);
|
||||
Assert.AreEqual(tc.RealVariable, tc2.RealVariable);
|
||||
Assert.AreEqual(tc.DWordVariable, tc2.DWordVariable);
|
||||
Assert.AreEqual(tc.WStringVariable, tc2.WStringVariable);
|
||||
Assert.AreEqual(tc.StringVariable, tc2.StringVariable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -783,6 +788,8 @@ namespace S7.Net.UnitTest
|
||||
ts.LRealVariable = -154.789;
|
||||
ts.RealVariable = -154.789f;
|
||||
ts.DWordVariable = 850;
|
||||
ts.WStringVariable = "ÄÜÉÊéà";
|
||||
ts.StringVariable = "Hallo";
|
||||
|
||||
plc.WriteStruct(ts, DB2);
|
||||
|
||||
@@ -797,6 +804,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(ts2.LRealVariable, ts2Generic.LRealVariable);
|
||||
Assert.AreEqual(ts2.RealVariable, ts2Generic.RealVariable);
|
||||
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
|
||||
Assert.AreEqual(ts2.WStringVariable, ts2Generic.WStringVariable);
|
||||
Assert.AreEqual(ts2.StringVariable, ts2Generic.StringVariable);
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
@@ -924,7 +933,14 @@ namespace S7.Net.UnitTest
|
||||
S7TestServer.Stop();
|
||||
|
||||
var unreachablePlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 2);
|
||||
Assert.IsFalse(unreachablePlc.IsAvailable);
|
||||
try
|
||||
{
|
||||
unreachablePlc.Open();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
Assert.IsFalse(unreachablePlc.IsConnected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -935,7 +951,8 @@ namespace S7.Net.UnitTest
|
||||
S7TestServer.Start(TestServerPort);
|
||||
|
||||
var reachablePlc = CreatePlc();
|
||||
Assert.IsTrue(reachablePlc.IsAvailable);
|
||||
reachablePlc.Open();
|
||||
Assert.IsTrue(reachablePlc.IsConnected);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
38
S7.Net.UnitTest/TypeTests/BooleanTests.cs
Normal file
38
S7.Net.UnitTest/TypeTests/BooleanTests.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Boolean = S7.Net.Types.Boolean;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class BooleanTests
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(1)]
|
||||
[DataRow(2)]
|
||||
[DataRow(3)]
|
||||
[DataRow(4)]
|
||||
[DataRow(5)]
|
||||
[DataRow(6)]
|
||||
[DataRow(7)]
|
||||
public void TestValidSetBitValues(int index)
|
||||
{
|
||||
Assert.AreEqual(Math.Pow(2, index), Boolean.SetBit(0, index));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0)]
|
||||
[DataRow(1)]
|
||||
[DataRow(2)]
|
||||
[DataRow(3)]
|
||||
[DataRow(4)]
|
||||
[DataRow(5)]
|
||||
[DataRow(6)]
|
||||
[DataRow(7)]
|
||||
public void TestValidClearBitValues(int index)
|
||||
{
|
||||
Assert.AreEqual((byte) ((uint) Math.Pow(2, index) ^ uint.MaxValue), Boolean.ClearBit(byte.MaxValue, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithStringLargetThanReservedLength()
|
||||
public void WriteAbcWithStringLargerThanReservedLength()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => S7String.ToByteArray("Abc", 2));
|
||||
}
|
||||
@@ -117,13 +117,24 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
AssertToByteArrayAndBackEquals("Abc", 4, 4, 3, (byte) 'A', (byte) 'b', (byte) 'c', 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OddS7StringByteLength()
|
||||
{
|
||||
AssertVarTypeToByteLength(VarType.S7String, 1, 4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EvenS7StringByteLength()
|
||||
{
|
||||
AssertVarTypeToByteLength(VarType.S7String, 2, 4);
|
||||
}
|
||||
|
||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||
{
|
||||
var convertedString = S7String.FromByteArray(bytes);
|
||||
Assert.AreEqual(expected, convertedString);
|
||||
}
|
||||
|
||||
|
||||
private static void AssertToByteArrayAndBackEquals(string value, int reservedLength, params byte[] expected)
|
||||
{
|
||||
var convertedData = S7String.ToByteArray(value, reservedLength);
|
||||
@@ -131,5 +142,11 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
var convertedBack = S7String.FromByteArray(convertedData);
|
||||
Assert.AreEqual(value, convertedBack);
|
||||
}
|
||||
|
||||
private void AssertVarTypeToByteLength(VarType varType, int count, int expectedByteLength)
|
||||
{
|
||||
var byteLength = Plc.VarTypeToByteLength(varType, count);
|
||||
Assert.AreEqual(expectedByteLength, byteLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
151
S7.Net.UnitTest/TypeTests/S7WStringTests.cs
Normal file
151
S7.Net.UnitTest/TypeTests/S7WStringTests.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net.Types;
|
||||
using System;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class S7WStringTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithZeroLength()
|
||||
{
|
||||
AssertFromByteArrayEquals("", 0, 0 , 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithOneCharLength()
|
||||
{
|
||||
AssertFromByteArrayEquals("", 0, 1, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithOneCharGarbage()
|
||||
{
|
||||
|
||||
AssertFromByteArrayEquals("", 0, 1, 0, 0, 0x00, 0x41);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadMalformedStringTooShort()
|
||||
{
|
||||
Assert.ThrowsException<PlcException>(() => AssertFromByteArrayEquals("", 0, 1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadMalformedStringSizeLargerThanCapacity()
|
||||
{
|
||||
Assert.ThrowsException<PlcException>(() => S7WString.FromByteArray(new byte[] { 0, 3, 0, 5, 0, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41}));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadMalformedStringCapacityTooLarge()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => AssertToByteArrayAndBackEquals("", 20000, 0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadA()
|
||||
{
|
||||
AssertFromByteArrayEquals("A", 0, 1, 0, 1, 0x00, 0x41);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAbc()
|
||||
{
|
||||
AssertFromByteArrayEquals("Abc", 0, 3, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteNullWithReservedLengthZero()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => AssertToByteArrayAndBackEquals(null, 0, 0, 0, 0, 0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("", 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("", 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteNullWithReservedLengthOne()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => AssertToByteArrayAndBackEquals(null, 1, 0, 1 , 0, 0));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("", 1, 0, 1, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("A", 1, 0, 1, 0, 1, 0x00, 0x41);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthTwo()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("A", 2, 0, 2, 0, 1, 0x00, 0x41, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithStringLargerThanReservedLength()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => S7WString.ToByteArray("Abc", 2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthThree()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("Abc", 3, 0, 3, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthFour()
|
||||
{
|
||||
AssertToByteArrayAndBackEquals("Abc", 4, 0, 4, 0, 3, 0x00, 0x41, 0x00, 0x62, 0x00, 0x63, 0 , 0);
|
||||
}
|
||||
|
||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||
{
|
||||
var convertedString = S7WString.FromByteArray(bytes);
|
||||
Assert.AreEqual(expected, convertedString);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OddS7WStringByteLength()
|
||||
{
|
||||
AssertVarTypeToByteLength(VarType.S7WString, 1, 6);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EvenS7WStringByteLength()
|
||||
{
|
||||
AssertVarTypeToByteLength(VarType.S7WString, 2, 8);
|
||||
}
|
||||
|
||||
private static void AssertToByteArrayAndBackEquals(string value, int reservedLength, params byte[] expected)
|
||||
{
|
||||
var convertedData = S7WString.ToByteArray(value, reservedLength);
|
||||
CollectionAssert.AreEqual(expected, convertedData);
|
||||
var convertedBack = S7WString.FromByteArray(convertedData);
|
||||
Assert.AreEqual(value, convertedBack);
|
||||
}
|
||||
|
||||
private void AssertVarTypeToByteLength(VarType varType, int count, int expectedByteLength)
|
||||
{
|
||||
var byteLength = Plc.VarTypeToByteLength(varType, count);
|
||||
Assert.AreEqual(expectedByteLength, byteLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,6 +178,11 @@
|
||||
/// </summary>
|
||||
S7String,
|
||||
|
||||
/// <summary>
|
||||
/// S7 WString variable type (variable)
|
||||
/// </summary>
|
||||
S7WString,
|
||||
|
||||
/// <summary>
|
||||
/// Timer variable type
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace S7.Net
|
||||
public partial class Plc : IDisposable
|
||||
{
|
||||
private const int CONNECTION_TIMED_OUT_ERROR_CODE = 10060;
|
||||
|
||||
|
||||
//TCP connection to device
|
||||
private TcpClient? tcpClient;
|
||||
private NetworkStream? _stream;
|
||||
@@ -75,47 +75,26 @@ namespace S7.Net
|
||||
if (tcpClient != null) tcpClient.SendTimeout = writeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a connection to the PLC can be established
|
||||
/// </summary>
|
||||
public bool IsAvailable
|
||||
{
|
||||
//TODO: Fix This
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
OpenAsync().GetAwaiter().GetResult();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the socket is connected and polls the other peer (the PLC) to see if it's connected.
|
||||
/// This is the variable that you should continously check to see if the communication is working
|
||||
/// See also: http://stackoverflow.com/questions/2661764/how-to-check-if-a-socket-is-connected-disconnected-in-c
|
||||
/// Gets a value indicating whether a connection to the PLC has been established.
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tcpClient == null)
|
||||
return false;
|
||||
|
||||
//TODO: Actually check communication by sending an empty TPDU
|
||||
return tcpClient.Connected;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
/// <remarks>
|
||||
/// The <see cref="IsConnected"/> property gets the connection state of the Client socket as
|
||||
/// of the last I/O operation. When it returns <c>false</c>, the Client socket was either
|
||||
/// never connected, or is no longer connected.
|
||||
///
|
||||
/// <para>
|
||||
/// Because the <see cref="IsConnected"/> property only reflects the state of the connection
|
||||
/// as of the most recent operation, you should attempt to send or receive a message to
|
||||
/// determine the current state. After the message send fails, this property no longer
|
||||
/// returns <c>true</c>. Note that this behavior is by design. You cannot reliably test the
|
||||
/// state of the connection because, in the time between the test and a send/receive, the
|
||||
/// connection could have been lost. Your code should assume the socket is connected, and
|
||||
/// gracefully handle failed transmissions.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool IsConnected => tcpClient?.Connected ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PLC object with all the parameters needed for connections.
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
case "Z":
|
||||
case "C":
|
||||
// Counter
|
||||
dataType = DataType.Timer;
|
||||
dataType = DataType.Counter;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(1));
|
||||
varType = VarType.Counter;
|
||||
@@ -204,4 +204,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// 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>
|
||||
@@ -125,6 +125,8 @@ namespace S7.Net
|
||||
return Types.String.FromByteArray(bytes);
|
||||
case VarType.S7String:
|
||||
return S7String.FromByteArray(bytes);
|
||||
case VarType.S7WString:
|
||||
return S7WString.FromByteArray(bytes);
|
||||
|
||||
case VarType.Timer:
|
||||
if (varCount == 1)
|
||||
@@ -182,13 +184,15 @@ namespace S7.Net
|
||||
switch (varType)
|
||||
{
|
||||
case VarType.Bit:
|
||||
return varCount + 7 / 8;
|
||||
return (varCount + 7) / 8;
|
||||
case VarType.Byte:
|
||||
return (varCount < 1) ? 1 : varCount;
|
||||
case VarType.String:
|
||||
return varCount;
|
||||
case VarType.S7String:
|
||||
return varCount + 2;
|
||||
return ((varCount + 2) & 1) == 1 ? (varCount + 3) : (varCount + 2);
|
||||
case VarType.S7WString:
|
||||
return (varCount * 2) + 4;
|
||||
case VarType.Word:
|
||||
case VarType.Timer:
|
||||
case VarType.Int:
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace S7.Net
|
||||
{
|
||||
//This works up to MaxPDUSize-1 on SNAP7. But not MaxPDUSize-0.
|
||||
var maxToRead = Math.Min(count, MaxPDUSize - 18);
|
||||
await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead, cancellationToken);
|
||||
await ReadBytesWithSingleRequestAsync(dataType, db, startByteAdr + index, resultBytes, index, maxToRead, cancellationToken).ConfigureAwait(false);
|
||||
count -= maxToRead;
|
||||
index += maxToRead;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace S7.Net
|
||||
public async Task<object?> ReadAsync(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0, CancellationToken cancellationToken = default)
|
||||
{
|
||||
int cntBytes = VarTypeToByteLength(varType, varCount);
|
||||
byte[] bytes = await ReadBytesAsync(dataType, db, startByteAdr, cntBytes, cancellationToken);
|
||||
byte[] bytes = await ReadBytesAsync(dataType, db, startByteAdr, cntBytes, cancellationToken).ConfigureAwait(false);
|
||||
return ParseBytes(varType, bytes, varCount, bitAdr);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace S7.Net
|
||||
public async Task<object?> ReadAsync(string variable, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
return await ReadAsync(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber, cancellationToken);
|
||||
return await ReadAsync(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,7 +158,7 @@ namespace S7.Net
|
||||
{
|
||||
int numBytes = Types.Struct.GetStructSize(structType);
|
||||
// now read the package
|
||||
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken);
|
||||
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// and decode it
|
||||
return Types.Struct.FromBytes(structType, resultBytes);
|
||||
@@ -175,7 +175,7 @@ namespace S7.Net
|
||||
/// <returns>Returns a nulable struct. If nothing was read null will be returned.</returns>
|
||||
public async Task<T?> ReadStructAsync<T>(int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : struct
|
||||
{
|
||||
return await ReadStructAsync(typeof(T), db, startByteAdr, cancellationToken) as T?;
|
||||
return await ReadStructAsync(typeof(T), db, startByteAdr, cancellationToken).ConfigureAwait(false) as T?;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,7 +197,7 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
// now read the package
|
||||
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken);
|
||||
var resultBytes = await ReadBytesAsync(DataType.DataBlock, db, startByteAdr, numBytes, cancellationToken).ConfigureAwait(false);
|
||||
// and decode it
|
||||
Class.FromBytes(sourceClass, resultBytes);
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace S7.Net
|
||||
/// <returns>An instance of the class with the values read from the PLC. If no data has been read, null will be returned</returns>
|
||||
public async Task<T?> ReadClassAsync<T>(int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
return await ReadClassAsync(() => Activator.CreateInstance<T>(), db, startByteAdr, cancellationToken);
|
||||
return await ReadClassAsync(() => Activator.CreateInstance<T>(), db, startByteAdr, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -234,7 +234,7 @@ namespace S7.Net
|
||||
public async Task<T?> ReadClassAsync<T>(Func<T> classFactory, int db, int startByteAdr = 0, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var instance = classFactory();
|
||||
var res = await ReadClassAsync(instance, db, startByteAdr, cancellationToken);
|
||||
var res = await ReadClassAsync(instance, db, startByteAdr, cancellationToken).ConfigureAwait(false);
|
||||
int readBytes = res.Item1;
|
||||
if (readBytes <= 0)
|
||||
{
|
||||
@@ -265,9 +265,9 @@ namespace S7.Net
|
||||
try
|
||||
{
|
||||
var dataToSend = BuildReadRequestPackage(dataItems.Select(d => DataItem.GetDataItemAddress(d)).ToList());
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length).ConfigureAwait(false);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
|
||||
ParseDataIntoDataItems(s7data, dataItems);
|
||||
@@ -306,7 +306,7 @@ namespace S7.Net
|
||||
while (count > 0)
|
||||
{
|
||||
var maxToWrite = (int)Math.Min(count, MaxPDUSize - 35);
|
||||
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite, cancellationToken);
|
||||
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value, localIndex, maxToWrite, cancellationToken).ConfigureAwait(false);
|
||||
count -= maxToWrite;
|
||||
localIndex += maxToWrite;
|
||||
}
|
||||
@@ -328,7 +328,7 @@ namespace S7.Net
|
||||
if (bitAdr < 0 || bitAdr > 7)
|
||||
throw new InvalidAddressException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr));
|
||||
|
||||
await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value, cancellationToken);
|
||||
await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -347,7 +347,7 @@ namespace S7.Net
|
||||
if (value < 0 || value > 1)
|
||||
throw new ArgumentException("Value must be 0 or 1", nameof(value));
|
||||
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1, cancellationToken);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -370,7 +370,7 @@ namespace S7.Net
|
||||
//Must be writing a bit value as bitAdr is specified
|
||||
if (value is bool boolean)
|
||||
{
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, boolean, cancellationToken);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, boolean, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (value is int intValue)
|
||||
{
|
||||
@@ -380,11 +380,11 @@ namespace S7.Net
|
||||
"Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid",
|
||||
bitAdr), nameof(bitAdr));
|
||||
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1, cancellationToken);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
|
||||
}
|
||||
else await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value), cancellationToken);
|
||||
else await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -399,7 +399,7 @@ namespace S7.Net
|
||||
public async Task WriteAsync(string variable, object value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
await WriteAsync(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber, cancellationToken);
|
||||
await WriteAsync(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -414,7 +414,7 @@ namespace S7.Net
|
||||
public async Task WriteStructAsync(object structValue, int db, int startByteAdr = 0, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var bytes = Struct.ToBytes(structValue).ToList();
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray(), cancellationToken);
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -430,7 +430,7 @@ namespace S7.Net
|
||||
{
|
||||
byte[] bytes = new byte[(int)Class.GetClassSize(classValue)];
|
||||
Types.Class.ToBytes(classValue, bytes);
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes, cancellationToken);
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
@@ -440,7 +440,7 @@ namespace S7.Net
|
||||
var dataToSend = BuildReadRequestPackage(new [] { new DataItemAddress(dataType, db, startByteAdr, count)});
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
AssertReadResponse(s7data, count);
|
||||
|
||||
Array.Copy(s7data, 18, buffer, offset, count);
|
||||
@@ -482,9 +482,9 @@ namespace S7.Net
|
||||
var stream = GetStreamIfAvailable();
|
||||
var dataToSend = BuildWriteBytesPackage(dataType, db, startByteAdr, value, dataOffset, count);
|
||||
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken);
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -505,9 +505,9 @@ namespace S7.Net
|
||||
{
|
||||
var dataToSend = BuildWriteBitPackage(dataType, db, startByteAdr, bitValue, bitAdr);
|
||||
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length).ConfigureAwait(false);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -17,10 +17,14 @@ namespace S7.Net.Protocol
|
||||
{
|
||||
throw new Exception($"DataItem.Value is null, cannot serialize. StartAddr={dataItem.StartByteAdr} VarType={dataItem.VarType}");
|
||||
}
|
||||
|
||||
if (dataItem.Value is string s)
|
||||
return dataItem.VarType == VarType.S7String
|
||||
? S7String.ToByteArray(s, dataItem.Count)
|
||||
: Types.String.ToByteArray(s, dataItem.Count);
|
||||
return dataItem.VarType switch
|
||||
{
|
||||
VarType.S7String => S7String.ToByteArray(s, dataItem.Count),
|
||||
VarType.S7WString => S7WString.ToByteArray(s, dataItem.Count),
|
||||
_ => Types.String.ToByteArray(s, dataItem.Count)
|
||||
};
|
||||
|
||||
return SerializeValue(dataItem.Value);
|
||||
}
|
||||
@@ -46,7 +50,7 @@ namespace S7.Net.Protocol
|
||||
case "Double":
|
||||
return Types.LReal.ToByteArray((double)value);
|
||||
case "DateTime":
|
||||
return Types.DateTime.ToByteArray((System.DateTime) value);
|
||||
return Types.DateTime.ToByteArray((System.DateTime)value);
|
||||
case "Byte[]":
|
||||
return (byte[])value;
|
||||
case "Int16[]":
|
||||
@@ -64,10 +68,10 @@ namespace S7.Net.Protocol
|
||||
case "String":
|
||||
// Hack: This is backwards compatible with the old code, but functionally it's broken
|
||||
// if the consumer does not pay attention to string length.
|
||||
var stringVal = (string) value;
|
||||
var stringVal = (string)value;
|
||||
return Types.String.ToByteArray(stringVal, stringVal.Length);
|
||||
case "DateTime[]":
|
||||
return Types.DateTime.ToByteArray((System.DateTime[]) value);
|
||||
return Types.DateTime.ToByteArray((System.DateTime[])value);
|
||||
case "DateTimeLong[]":
|
||||
return Types.DateTimeLong.ToByteArray((System.DateTime[])value);
|
||||
default:
|
||||
|
||||
@@ -14,20 +14,51 @@
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a bit to 1 (true), given the address of the bit
|
||||
/// Sets the value of a bit to 1 (true), given the address of the bit. Returns
|
||||
/// a copy of the value with the bit set.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value to modify.</param>
|
||||
/// <param name="bit">The index (zero based) of the bit to set.</param>
|
||||
/// <returns>The modified value with the bit at index set.</returns>
|
||||
public static byte SetBit(byte value, int bit)
|
||||
{
|
||||
return (byte)((value | (1 << bit)) & 0xFF);
|
||||
SetBit(ref value, bit);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of a bit to 1 (true), given the address of the bit.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to modify.</param>
|
||||
/// <param name="bit">The index (zero based) of the bit to set.</param>
|
||||
public static void SetBit(ref byte value, int bit)
|
||||
{
|
||||
value = (byte) ((value | (1 << bit)) & 0xFF);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value of a bit to 0 (false), given the address of the bit. Returns
|
||||
/// a copy of the value with the bit cleared.
|
||||
/// </summary>
|
||||
/// <param name="value">The input value to modify.</param>
|
||||
/// <param name="bit">The index (zero based) of the bit to clear.</param>
|
||||
/// <returns>The modified value with the bit at index cleared.</returns>
|
||||
public static byte ClearBit(byte value, int bit)
|
||||
{
|
||||
ClearBit(ref value, bit);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value of a bit to 0 (false), given the address of the bit
|
||||
/// </summary>
|
||||
public static byte ClearBit(byte value, int bit)
|
||||
/// <param name="value">The input value to modify.</param>
|
||||
/// <param name="bit">The index (zero based) of the bit to clear.</param>
|
||||
public static void ClearBit(ref byte value, int bit)
|
||||
{
|
||||
return (byte)((value | (~(1 << bit))) & 0xFF);
|
||||
value = (byte) (value & ~(1 << bit) & 0xFF);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace S7.Net.Types
|
||||
/// Converts a <see cref="T:string"/> to S7 string with 2-byte header.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert to byte array.</param>
|
||||
/// <param name="reservedLength">The length (in bytes) allocated in PLC for string excluding header.</param>
|
||||
/// <param name="reservedLength">The length (in characters) allocated in PLC for the string.</param>
|
||||
/// <returns>A <see cref="T:byte[]" /> containing the string header and string value with a maximum length of <paramref name="reservedLength"/> + 2.</returns>
|
||||
public static byte[] ToByteArray(string value, int reservedLength)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace S7.Net.Types
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}.");
|
||||
if (reservedLength > 254) throw new ArgumentException($"The maximum string length supported is 254.");
|
||||
|
||||
var bytes = Encoding.ASCII.GetBytes(value);
|
||||
if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength}).");
|
||||
|
||||
67
S7.Net/Types/S7StringAttribute.cs
Normal file
67
S7.Net/Types/S7StringAttribute.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public sealed class S7StringAttribute : Attribute
|
||||
{
|
||||
private readonly S7StringType type;
|
||||
private readonly int reservedLength;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="S7StringAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The string type.</param>
|
||||
/// <param name="reservedLength">Reserved length of the string in characters.</param>
|
||||
/// <exception cref="ArgumentException">Please use a valid value for the string type</exception>
|
||||
public S7StringAttribute(S7StringType type, int reservedLength)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(S7StringType), type))
|
||||
throw new ArgumentException("Please use a valid value for the string type");
|
||||
|
||||
this.type = type;
|
||||
this.reservedLength = reservedLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the string.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The string type.
|
||||
/// </value>
|
||||
public S7StringType Type => type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reserved length of the string in characters.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The reserved length of the string in characters.
|
||||
/// </value>
|
||||
public int ReservedLength => reservedLength;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reserved length in bytes.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The reserved length in bytes.
|
||||
/// </value>
|
||||
public int ReservedLengthInBytes => type == S7StringType.S7String ? reservedLength + 2 : (reservedLength * 2) + 4;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// String type.
|
||||
/// </summary>
|
||||
public enum S7StringType
|
||||
{
|
||||
/// <summary>
|
||||
/// ASCII string.
|
||||
/// </summary>
|
||||
S7String = VarType.S7String,
|
||||
|
||||
/// <summary>
|
||||
/// Unicode string.
|
||||
/// </summary>
|
||||
S7WString = VarType.S7WString
|
||||
}
|
||||
}
|
||||
72
S7.Net/Types/S7WString.cs
Normal file
72
S7.Net/Types/S7WString.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the methods to convert from S7 wstrings to C# strings
|
||||
/// An S7 WString has a preceding 4 byte header containing its capacity and length
|
||||
/// </summary>
|
||||
public static class S7WString
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts S7 bytes to a string
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <returns></returns>
|
||||
public static string FromByteArray(byte[] bytes)
|
||||
{
|
||||
if (bytes.Length < 4)
|
||||
{
|
||||
throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / too short");
|
||||
}
|
||||
|
||||
int size = (bytes[0] << 8) | bytes[1];
|
||||
int length = (bytes[2] << 8) | bytes[3];
|
||||
|
||||
if (length > size)
|
||||
{
|
||||
throw new PlcException(ErrorCode.ReadData, "Malformed S7 WString / length larger than capacity");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return Encoding.BigEndianUnicode.GetString(bytes, 4, length * 2);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new PlcException(ErrorCode.ReadData,
|
||||
$"Failed to parse {VarType.S7WString} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.",
|
||||
e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="T:string"/> to S7 wstring with 4-byte header.
|
||||
/// </summary>
|
||||
/// <param name="value">The string to convert to byte array.</param>
|
||||
/// <param name="reservedLength">The length (in characters) allocated in PLC for the string.</param>
|
||||
/// <returns>A <see cref="T:byte[]" /> containing the string header and string value with a maximum length of <paramref name="reservedLength"/> + 4.</returns>
|
||||
public static byte[] ToByteArray(string value, int reservedLength)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (reservedLength > 16382) throw new ArgumentException("The maximum string length supported is 16382.");
|
||||
|
||||
var buffer = new byte[4 + reservedLength * 2];
|
||||
buffer[0] = (byte)((reservedLength >> 8) & 0xFF);
|
||||
buffer[1] = (byte)(reservedLength & 0xFF);
|
||||
buffer[2] = (byte)((value.Length >> 8) & 0xFF);
|
||||
buffer[3] = (byte)(value.Length & 0xFF);
|
||||
|
||||
var stringLength = Encoding.BigEndianUnicode.GetBytes(value, 0, value.Length, buffer, 4) / 2;
|
||||
if (stringLength > reservedLength) throw new ArgumentException($"The provided string length ({stringLength} is larger than the specified reserved length ({reservedLength}).");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace S7.Net.Types
|
||||
@@ -18,11 +19,11 @@ namespace S7.Net.Types
|
||||
double numBytes = 0.0;
|
||||
|
||||
var infos = structType
|
||||
#if NETSTANDARD1_3
|
||||
#if NETSTANDARD1_3
|
||||
.GetTypeInfo().DeclaredFields;
|
||||
#else
|
||||
#else
|
||||
.GetFields();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
foreach (var info in infos)
|
||||
{
|
||||
@@ -61,6 +62,16 @@ namespace S7.Net.Types
|
||||
numBytes++;
|
||||
numBytes += 8;
|
||||
break;
|
||||
case "String":
|
||||
S7StringAttribute? attribute = info.GetCustomAttributes<S7StringAttribute>().SingleOrDefault();
|
||||
if (attribute == default(S7StringAttribute))
|
||||
throw new ArgumentException("Please add S7StringAttribute to the string field");
|
||||
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
numBytes += attribute.ReservedLengthInBytes;
|
||||
break;
|
||||
default:
|
||||
numBytes += GetStructSize(info.FieldType);
|
||||
break;
|
||||
@@ -91,11 +102,11 @@ namespace S7.Net.Types
|
||||
|
||||
|
||||
var infos = structValue.GetType()
|
||||
#if NETSTANDARD1_3
|
||||
#if NETSTANDARD1_3
|
||||
.GetTypeInfo().DeclaredFields;
|
||||
#else
|
||||
#else
|
||||
.GetFields();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
foreach (var info in infos)
|
||||
{
|
||||
@@ -120,7 +131,7 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
ushort source = Word.FromBytes(bytes[(int)numBytes + 1], bytes[(int)numBytes]);
|
||||
info.SetValue(structValue, source.ConvertToShort());
|
||||
numBytes += 2;
|
||||
@@ -129,7 +140,7 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
info.SetValue(structValue, Word.FromBytes(bytes[(int)numBytes + 1],
|
||||
bytes[(int)numBytes]));
|
||||
numBytes += 2;
|
||||
@@ -138,7 +149,7 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
uint sourceUInt = DWord.FromBytes(bytes[(int)numBytes + 3],
|
||||
bytes[(int)numBytes + 2],
|
||||
bytes[(int)numBytes + 1],
|
||||
@@ -150,7 +161,7 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
info.SetValue(structValue, DWord.FromBytes(bytes[(int)numBytes],
|
||||
bytes[(int)numBytes + 1],
|
||||
bytes[(int)numBytes + 2],
|
||||
@@ -161,7 +172,7 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
info.SetValue(structValue, Real.FromByteArray(new byte[] { bytes[(int)numBytes],
|
||||
bytes[(int)numBytes + 1],
|
||||
bytes[(int)numBytes + 2],
|
||||
@@ -172,12 +183,38 @@ namespace S7.Net.Types
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
// hier auswerten
|
||||
// get the value
|
||||
var data = new byte[8];
|
||||
Array.Copy(bytes, (int)numBytes, data, 0, 8);
|
||||
info.SetValue(structValue, LReal.FromByteArray(data));
|
||||
numBytes += 8;
|
||||
break;
|
||||
case "String":
|
||||
S7StringAttribute? attribute = info.GetCustomAttributes<S7StringAttribute>().SingleOrDefault();
|
||||
if (attribute == default(S7StringAttribute))
|
||||
throw new ArgumentException("Please add S7StringAttribute to the string field");
|
||||
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
|
||||
// get the value
|
||||
var sData = new byte[attribute.ReservedLengthInBytes];
|
||||
Array.Copy(bytes, (int)numBytes, sData, 0, sData.Length);
|
||||
switch (attribute.Type)
|
||||
{
|
||||
case S7StringType.S7String:
|
||||
info.SetValue(structValue, S7String.FromByteArray(sData));
|
||||
break;
|
||||
case S7StringType.S7WString:
|
||||
info.SetValue(structValue, S7WString.FromByteArray(sData));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Please use a valid string type for the S7StringAttribute");
|
||||
}
|
||||
|
||||
numBytes += sData.Length;
|
||||
break;
|
||||
default:
|
||||
var buffer = new byte[GetStructSize(info.FieldType)];
|
||||
if (buffer.Length == 0)
|
||||
@@ -209,11 +246,11 @@ namespace S7.Net.Types
|
||||
double numBytes = 0.0;
|
||||
|
||||
var infos = type
|
||||
#if NETSTANDARD1_3
|
||||
#if NETSTANDARD1_3
|
||||
.GetTypeInfo().DeclaredFields;
|
||||
#else
|
||||
#else
|
||||
.GetFields();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
foreach (var info in infos)
|
||||
{
|
||||
@@ -254,6 +291,18 @@ namespace S7.Net.Types
|
||||
case "Double":
|
||||
bytes2 = LReal.ToByteArray((double)info.GetValue(structValue));
|
||||
break;
|
||||
case "String":
|
||||
S7StringAttribute? attribute = info.GetCustomAttributes<S7StringAttribute>().SingleOrDefault();
|
||||
if (attribute == default(S7StringAttribute))
|
||||
throw new ArgumentException("Please add S7StringAttribute to the string field");
|
||||
|
||||
bytes2 = attribute.Type switch
|
||||
{
|
||||
S7StringType.S7String => S7String.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength),
|
||||
S7StringType.S7WString => S7WString.ToByteArray((string)info.GetValue(structValue), attribute.ReservedLength),
|
||||
_ => throw new ArgumentException("Please use a valid string type for the S7StringAttribute")
|
||||
};
|
||||
break;
|
||||
}
|
||||
if (bytes2 != null)
|
||||
{
|
||||
@@ -269,7 +318,5 @@ namespace S7.Net.Types
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user