mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-25 08:48:25 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc6781c37f | ||
|
|
3555436c04 | ||
|
|
3258c84fbc | ||
|
|
28257f28b3 | ||
|
|
a1d87de2d9 | ||
|
|
4be5765fc9 | ||
|
|
6614c7330a | ||
|
|
5d59c8284d | ||
|
|
6554b999c0 | ||
|
|
ff20687776 | ||
|
|
0b6226327b | ||
|
|
385240ba5e | ||
|
|
0a8ee0e091 | ||
|
|
1685270535 | ||
|
|
9a34b14e1e | ||
|
|
50f0e62573 | ||
|
|
9ea54be524 | ||
|
|
dcd5bb3437 | ||
|
|
8dc89867e9 |
156
S7.Net.UnitTest/PLCAddressParsingTests.cs
Normal file
156
S7.Net.UnitTest/PLCAddressParsingTests.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net.Types;
|
||||
using System;
|
||||
|
||||
namespace S7.Net.UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class PLCAddressParsingTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void T01_ParseM2000_1()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("M2000.1");
|
||||
|
||||
Assert.AreEqual(DataType.Memory, dataItem.DataType, "Wrong datatype for M2000.1");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for M2000.1");
|
||||
Assert.AreEqual(VarType.Bit, dataItem.VarType, "Wrong vartype for M2000.1");
|
||||
Assert.AreEqual(2000, dataItem.StartByteAdr, "Wrong startbyte for M2000.1");
|
||||
Assert.AreEqual(1, dataItem.BitAdr, "Wrong bit for M2000.1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T02_ParseMB200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("MB200");
|
||||
|
||||
Assert.AreEqual(DataType.Memory, dataItem.DataType, "Wrong datatype for MB200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for MB200");
|
||||
Assert.AreEqual(VarType.Byte, dataItem.VarType, "Wrong vartype for MB200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for MB200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for MB200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T03_ParseMW200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("MW200");
|
||||
|
||||
Assert.AreEqual(DataType.Memory, dataItem.DataType, "Wrong datatype for MW200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for MW200");
|
||||
Assert.AreEqual(VarType.Word, dataItem.VarType, "Wrong vartype for MW200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for MW200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for MW200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T04_ParseMD200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("MD200");
|
||||
|
||||
Assert.AreEqual(DataType.Memory, dataItem.DataType, "Wrong datatype for MD200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for MD200");
|
||||
Assert.AreEqual(VarType.DWord, dataItem.VarType, "Wrong vartype for MD200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for MD200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for MD200");
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void T05_ParseI2000_1()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("I2000.1");
|
||||
|
||||
Assert.AreEqual(DataType.Input, dataItem.DataType, "Wrong datatype for I2000.1");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for I2000.1");
|
||||
Assert.AreEqual(VarType.Bit, dataItem.VarType, "Wrong vartype for I2000.1");
|
||||
Assert.AreEqual(2000, dataItem.StartByteAdr, "Wrong startbyte for I2000.1");
|
||||
Assert.AreEqual(1, dataItem.BitAdr, "Wrong bit for I2000.1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T06_ParseIB200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("IB200");
|
||||
|
||||
Assert.AreEqual(DataType.Input, dataItem.DataType, "Wrong datatype for IB200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for IB200");
|
||||
Assert.AreEqual(VarType.Byte, dataItem.VarType, "Wrong vartype for IB200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for IB200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for IB200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T07_ParseIW200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("IW200");
|
||||
|
||||
Assert.AreEqual(DataType.Input, dataItem.DataType, "Wrong datatype for IW200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for IW200");
|
||||
Assert.AreEqual(VarType.Word, dataItem.VarType, "Wrong vartype for IW200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for IW200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for IW200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T08_ParseID200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("ID200");
|
||||
|
||||
Assert.AreEqual(DataType.Input, dataItem.DataType, "Wrong datatype for ID200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for ID200");
|
||||
Assert.AreEqual(VarType.DWord, dataItem.VarType, "Wrong vartype for ID200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for ID200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for ID200");
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void T09_ParseQ2000_1()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("Q2000.1");
|
||||
|
||||
Assert.AreEqual(DataType.Output, dataItem.DataType, "Wrong datatype for Q2000.1");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for Q2000.1");
|
||||
Assert.AreEqual(VarType.Bit, dataItem.VarType, "Wrong vartype for Q2000.1");
|
||||
Assert.AreEqual(2000, dataItem.StartByteAdr, "Wrong startbyte for Q2000.1");
|
||||
Assert.AreEqual(1, dataItem.BitAdr, "Wrong bit for Q2000.1");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T10_ParseQB200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("QB200");
|
||||
|
||||
Assert.AreEqual(DataType.Output, dataItem.DataType, "Wrong datatype for QB200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for QB200");
|
||||
Assert.AreEqual(VarType.Byte, dataItem.VarType, "Wrong vartype for QB200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for QB200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for QB200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T11_ParseQW200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("QW200");
|
||||
|
||||
Assert.AreEqual(DataType.Output, dataItem.DataType, "Wrong datatype for QW200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for QW200");
|
||||
Assert.AreEqual(VarType.Word, dataItem.VarType, "Wrong vartype for QW200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for QW200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for QW200");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T12_ParseQD200()
|
||||
{
|
||||
DataItem dataItem = DataItem.FromAddress("QD200");
|
||||
|
||||
Assert.AreEqual(DataType.Output, dataItem.DataType, "Wrong datatype for QD200");
|
||||
Assert.AreEqual(0, dataItem.DB, "Wrong dbnumber for QD200");
|
||||
Assert.AreEqual(VarType.DWord, dataItem.VarType, "Wrong vartype for QD200");
|
||||
Assert.AreEqual(200, dataItem.StartByteAdr, "Wrong startbyte for QD200");
|
||||
Assert.AreEqual(0, dataItem.BitAdr, "Wrong bit for QD200");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@
|
||||
<Compile Include="ConnectionRequestTest.cs" />
|
||||
<Compile Include="ConvertersUnitTest.cs" />
|
||||
<Compile Include="Helpers\TestClassWithNestedClass.cs" />
|
||||
<Compile Include="PLCAddressParsingTests.cs" />
|
||||
<Compile Include="ProtocolTests.cs" />
|
||||
<Compile Include="Helpers\ConsoleManager.cs" />
|
||||
<Compile Include="Helpers\NativeMethods.cs" />
|
||||
@@ -79,6 +80,8 @@
|
||||
<Compile Include="S7NetTestsSync.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Helpers\TestLongStruct.cs" />
|
||||
<Compile Include="TypeTests\ClassTests.cs" />
|
||||
<Compile Include="TypeTests\DateTimeLongTests.cs" />
|
||||
<Compile Include="TypeTests\DateTimeTests.cs" />
|
||||
<Compile Include="TypeTests\StringExTests.cs" />
|
||||
<Compile Include="TypeTests\StringTests.cs" />
|
||||
|
||||
@@ -997,6 +997,20 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(test_value, test_value2, "Compare Write/Read"); //No delta, datatype matches
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T33_ReadWriteDateTimeLong()
|
||||
{
|
||||
var test_value = System.DateTime.Now;
|
||||
var db = 1;
|
||||
var offset = 0;
|
||||
|
||||
plc.WriteBytes(DataType.DataBlock, db, offset, Types.DateTimeLong.ToByteArray(test_value));
|
||||
var test_value2 = plc.Read(DataType.DataBlock, db, offset, VarType.DateTimeLong, 1);
|
||||
Assert.IsInstanceOfType(test_value2, typeof(System.DateTime));
|
||||
|
||||
Assert.AreEqual(test_value, test_value2, "Compare DateTimeLong Write/Read");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
33
S7.Net.UnitTest/TypeTests/ClassTests.cs
Normal file
33
S7.Net.UnitTest/TypeTests/ClassTests.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net.Types;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class ClassTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetClassSizeTest()
|
||||
{
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(1, 1)), 6);
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(2, 15)), 6);
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(2, 16)), 6);
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(2, 17)), 8);
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(3, 15)), 8);
|
||||
Assert.AreEqual(Class.GetClassSize(new TestClassUnevenSize(3, 17)), 10);
|
||||
}
|
||||
|
||||
private class TestClassUnevenSize
|
||||
{
|
||||
public bool Bool { get; set; }
|
||||
public byte[] Bytes { get; set; }
|
||||
public bool[] Bools { get; set; }
|
||||
|
||||
public TestClassUnevenSize(int byteCount, int bitCount)
|
||||
{
|
||||
Bytes = new byte[byteCount];
|
||||
Bools = new bool[bitCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
171
S7.Net.UnitTest/TypeTests/DateTimeLongTests.cs
Normal file
171
S7.Net.UnitTest/TypeTests/DateTimeLongTests.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
public static class DateTimeLongTests
|
||||
{
|
||||
private static readonly DateTime SampleDateTime = new DateTime(1993, 12, 25, 8, 12, 34, 567);
|
||||
|
||||
private static readonly byte[] SampleByteArray = {0x07, 0xC9, 0x0C, 0x19, 0x07, 0x08, 0x0C, 0x22, 0x21, 0xCB, 0xBB, 0xC0 };
|
||||
|
||||
private static readonly byte[] SpecMinByteArray =
|
||||
{
|
||||
0x07, 0xB2, 0x01, 0x01, (byte) (int) (Types.DateTimeLong.SpecMinimumDateTime.DayOfWeek + 1), 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
private static readonly byte[] SpecMaxByteArray =
|
||||
{
|
||||
0x08, 0xD6, 0x04, 0x0B, (byte) (int) (Types.DateTimeLong.SpecMaximumDateTime.DayOfWeek + 1), 0x17, 0x2F, 0x10, 0x32, 0xE7, 0x01, 0x80
|
||||
};
|
||||
|
||||
[TestClass]
|
||||
public class FromByteArray
|
||||
{
|
||||
[TestMethod]
|
||||
public void Sample()
|
||||
{
|
||||
AssertFromByteArrayEquals(SampleDateTime, SampleByteArray);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SpecMinimum()
|
||||
{
|
||||
AssertFromByteArrayEquals(Types.DateTimeLong.SpecMinimumDateTime, SpecMinByteArray);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SpecMaximum()
|
||||
{
|
||||
AssertFromByteArrayEquals(Types.DateTimeLong.SpecMaximumDateTime, SpecMaxByteArray);
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnLessThan12Bytes()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(new byte[11]);
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnMoreTHan12Bytes()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(new byte[13]);
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnInvalidYear()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(0, 0xa0));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnZeroMonth()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(2, 0x00));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnTooLargeMonth()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(2, 0x13));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnZeroDay()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(3, 0x00));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnTooLargeDay()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(3, 0x32));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnInvalidHour()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(5, 0x24));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnInvalidMinute()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(6, 0x60));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnInvalidSecond()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(7, 0x60));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnInvalidNanosecondsFirstDigit()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(8, 0x3B));
|
||||
}
|
||||
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnZeroDayOfWeek()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(4, 0));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnTooLargeDayOfWeek()
|
||||
{
|
||||
Types.DateTimeLong.FromByteArray(MutateSample(4, 8));
|
||||
}
|
||||
|
||||
private static void AssertFromByteArrayEquals(DateTime expected, params byte[] bytes)
|
||||
{
|
||||
Assert.AreEqual(expected, Types.DateTimeLong.FromByteArray(bytes));
|
||||
}
|
||||
|
||||
private static byte[] MutateSample(int index, byte value) =>
|
||||
SampleByteArray.Select((b, i) => i == index ? value : b).ToArray();
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class ToByteArray
|
||||
{
|
||||
[TestMethod]
|
||||
public void Sample()
|
||||
{
|
||||
AssertToByteArrayEquals(SampleDateTime, SampleByteArray);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SpecMinimum()
|
||||
{
|
||||
AssertToByteArrayEquals(Types.DateTimeLong.SpecMinimumDateTime, SpecMinByteArray);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SpecMaximum()
|
||||
{
|
||||
AssertToByteArrayEquals(Types.DateTimeLong.SpecMaximumDateTime, SpecMaxByteArray);
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnTimeBeforeSpecMinimum()
|
||||
{
|
||||
Types.DateTimeLong.ToByteArray(new DateTime(1950, 1, 1));
|
||||
}
|
||||
|
||||
[TestMethod, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void ThrowsOnTimeAfterSpecMaximum()
|
||||
{
|
||||
Types.DateTimeLong.ToByteArray(new DateTime(2790, 1, 1));
|
||||
}
|
||||
|
||||
private static void AssertToByteArrayEquals(DateTime value, params byte[] expected)
|
||||
{
|
||||
CollectionAssert.AreEqual(expected, Types.DateTimeLong.ToByteArray(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,6 +186,11 @@
|
||||
/// <summary>
|
||||
/// DateTIme variable type
|
||||
/// </summary>
|
||||
DateTime
|
||||
DateTime,
|
||||
|
||||
/// <summary>
|
||||
/// DateTimeLong variable type
|
||||
/// </summary>
|
||||
DateTimeLong
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,27 @@ namespace S7.Net
|
||||
.Sum(len => (len & 1) == 1 ? len + 1 : len);
|
||||
}
|
||||
|
||||
private static void AssertReadResponse(byte[] s7Data, int dataLength)
|
||||
{
|
||||
var expectedLength = dataLength + 18;
|
||||
|
||||
PlcException NotEnoughBytes() =>
|
||||
new PlcException(ErrorCode.WrongNumberReceivedBytes,
|
||||
$"Received {s7Data.Length} bytes: '{BitConverter.ToString(s7Data)}', expected {expectedLength} bytes.")
|
||||
;
|
||||
|
||||
if (s7Data == null)
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes, "No s7Data received.");
|
||||
|
||||
if (s7Data.Length < 15) throw NotEnoughBytes();
|
||||
|
||||
if (s7Data[14] != 0xff)
|
||||
throw new PlcException(ErrorCode.ReadData,
|
||||
$"Invalid response from PLC: '{BitConverter.ToString(s7Data)}'.");
|
||||
|
||||
if (s7Data.Length < expectedLength) throw NotEnoughBytes();
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
default:
|
||||
throw new InvalidAddressException();
|
||||
}
|
||||
case "IB":
|
||||
case "EB":
|
||||
// Input byte
|
||||
dataType = DataType.Input;
|
||||
@@ -87,6 +88,7 @@
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "IW":
|
||||
case "EW":
|
||||
// Input word
|
||||
dataType = DataType.Input;
|
||||
@@ -94,6 +96,7 @@
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "ID":
|
||||
case "ED":
|
||||
// Input double-word
|
||||
dataType = DataType.Input;
|
||||
@@ -101,21 +104,27 @@
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "QB":
|
||||
case "AB":
|
||||
case "OB":
|
||||
// Output byte
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "QW":
|
||||
case "AW":
|
||||
case "OW":
|
||||
// Output word
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "QD":
|
||||
case "AD":
|
||||
case "OD":
|
||||
// Output double-word
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
@@ -152,6 +161,7 @@
|
||||
dataType = DataType.Input;
|
||||
varType = VarType.Bit;
|
||||
break;
|
||||
case "Q":
|
||||
case "A":
|
||||
case "O":
|
||||
// Output
|
||||
@@ -161,7 +171,7 @@
|
||||
case "M":
|
||||
// Memory
|
||||
dataType = DataType.Memory;
|
||||
varType = VarType.Byte;
|
||||
varType = VarType.Bit;
|
||||
break;
|
||||
case "T":
|
||||
// Timer
|
||||
|
||||
@@ -156,6 +156,15 @@ namespace S7.Net
|
||||
{
|
||||
return DateTime.ToArray(bytes);
|
||||
}
|
||||
case VarType.DateTimeLong:
|
||||
if (varCount == 1)
|
||||
{
|
||||
return DateTimeLong.FromByteArray(bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DateTimeLong.ToArray(bytes);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -190,6 +199,8 @@ namespace S7.Net
|
||||
return varCount * 4;
|
||||
case VarType.DateTime:
|
||||
return varCount * 8;
|
||||
case VarType.DateTimeLong:
|
||||
return varCount * 12;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -388,8 +388,7 @@ namespace S7.Net
|
||||
await stream.WriteAsync(package.Array, 0, package.Array.Length);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
AssertReadResponse(s7data, count);
|
||||
|
||||
for (int cnt = 0; cnt < count; cnt++)
|
||||
bytes[cnt] = s7data[cnt + 18];
|
||||
|
||||
@@ -353,8 +353,7 @@ namespace S7.Net
|
||||
stream.Write(package.Array, 0, package.Array.Length);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
AssertReadResponse(s7data, count);
|
||||
|
||||
for (int cnt = 0; cnt < count; cnt++)
|
||||
bytes[cnt] = s7data[cnt + 18];
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace S7.Net.Protocol
|
||||
return Types.String.ToByteArray(stringVal, stringVal.Length);
|
||||
case "DateTime[]":
|
||||
return Types.DateTime.ToByteArray((System.DateTime[]) value);
|
||||
case "DateTimeLong[]":
|
||||
return Types.DateTimeLong.ToByteArray((System.DateTime[])value);
|
||||
default:
|
||||
throw new InvalidVariableTypeException();
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ namespace S7.Net.Types
|
||||
throw new Exception("Cannot determine size of class, because an array is defined which has no fixed size greater than zero.");
|
||||
}
|
||||
|
||||
IncrementToEven(ref numBytes);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
numBytes = GetIncreasedNumberOfBytes(numBytes, elementType);
|
||||
@@ -219,6 +220,7 @@ namespace S7.Net.Types
|
||||
if (property.PropertyType.IsArray)
|
||||
{
|
||||
Array array = (Array)property.GetValue(sourceClass, null);
|
||||
IncrementToEven(ref numBytes);
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
for (int i = 0; i < array.Length && numBytes < bytes.Length; i++)
|
||||
{
|
||||
@@ -288,10 +290,8 @@ namespace S7.Net.Types
|
||||
|
||||
if (bytes2 != null)
|
||||
{
|
||||
// add them
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if ((numBytes / 2 - Math.Floor(numBytes / 2.0)) > 0)
|
||||
numBytes++;
|
||||
IncrementToEven(ref numBytes);
|
||||
|
||||
bytePos = (int)numBytes;
|
||||
for (int bCnt = 0; bCnt < bytes2.Length; bCnt++)
|
||||
bytes[bytePos + bCnt] = bytes2[bCnt];
|
||||
@@ -314,6 +314,7 @@ namespace S7.Net.Types
|
||||
if (property.PropertyType.IsArray)
|
||||
{
|
||||
Array array = (Array)property.GetValue(sourceClass, null);
|
||||
IncrementToEven(ref numBytes);
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
for (int i = 0; i < array.Length && numBytes < bytes.Length; i++)
|
||||
{
|
||||
@@ -327,5 +328,11 @@ namespace S7.Net.Types
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
private static void IncrementToEven(ref double numBytes)
|
||||
{
|
||||
numBytes = Math.Ceiling(numBytes);
|
||||
if (numBytes % 2 > 0) numBytes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
185
S7.Net/Types/DateTimeLong.cs
Normal file
185
S7.Net/Types/DateTimeLong.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the methods to convert between <see cref="T:System.DateTime" /> and S7 representation of DateTimeLong (DTL) values.
|
||||
/// </summary>
|
||||
public static class DateTimeLong
|
||||
{
|
||||
public const int TypeLengthInBytes = 12;
|
||||
/// <summary>
|
||||
/// The minimum <see cref="T:System.DateTime" /> value supported by the specification.
|
||||
/// </summary>
|
||||
public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1970, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum <see cref="T:System.DateTime" /> value supported by the specification.
|
||||
/// </summary>
|
||||
public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2262, 4, 11, 23, 47, 16, 854);
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="T:System.DateTime" /> value from bytes.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Input bytes read from PLC.</param>
|
||||
/// <returns>A <see cref="T:System.DateTime" /> object representing the value read from PLC.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the length of
|
||||
/// <paramref name="bytes" /> is not 12 or any value in <paramref name="bytes" />
|
||||
/// is outside the valid range of values.
|
||||
/// </exception>
|
||||
public static System.DateTime FromByteArray(byte[] bytes)
|
||||
{
|
||||
return FromByteArrayImpl(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an array of <see cref="T:System.DateTime" /> values from bytes.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Input bytes read from PLC.</param>
|
||||
/// <returns>An array of <see cref="T:System.DateTime" /> objects representing the values read from PLC.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the length of
|
||||
/// <paramref name="bytes" /> is not a multiple of 12 or any value in
|
||||
/// <paramref name="bytes" /> is outside the valid range of values.
|
||||
/// </exception>
|
||||
public static System.DateTime[] ToArray(byte[] bytes)
|
||||
{
|
||||
if (bytes.Length % TypeLengthInBytes != 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
|
||||
$"Parsing an array of DateTimeLong requires a multiple of 12 bytes of input data, input data is '{bytes.Length}' long.");
|
||||
}
|
||||
|
||||
var cnt = bytes.Length / TypeLengthInBytes;
|
||||
var result = new System.DateTime[cnt];
|
||||
|
||||
for (var i = 0; i < cnt; i++)
|
||||
{
|
||||
var slice = new byte[TypeLengthInBytes];
|
||||
Array.Copy(bytes, i * TypeLengthInBytes, slice, 0, TypeLengthInBytes);
|
||||
result[i] = FromByteArrayImpl(slice);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static System.DateTime FromByteArrayImpl(byte[] bytes)
|
||||
{
|
||||
if (bytes.Length != TypeLengthInBytes)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
|
||||
$"Parsing a DateTimeLong requires exactly 12 bytes of input data, input data is {bytes.Length} bytes long.");
|
||||
}
|
||||
|
||||
|
||||
var year = AssertRangeInclusive(Word.FromBytes(bytes[1], bytes[0]), 1970, 2262, "year");
|
||||
var month = AssertRangeInclusive(bytes[2], 1, 12, "month");
|
||||
var day = AssertRangeInclusive(bytes[3], 1, 31, "day of month");
|
||||
var dayOfWeek = AssertRangeInclusive(bytes[4], 1, 7, "day of week");
|
||||
var hour = AssertRangeInclusive(bytes[5], 0, 23, "hour");
|
||||
var minute = AssertRangeInclusive(bytes[6], 0, 59, "minute");
|
||||
var second = AssertRangeInclusive(bytes[7], 0, 59, "second");
|
||||
;
|
||||
|
||||
var nanoseconds = AssertRangeInclusive<uint>(DWord.FromBytes(bytes[11], bytes[10], bytes[9], bytes[8]), 0,
|
||||
999999999, "nanoseconds");
|
||||
|
||||
var time = new System.DateTime(year, month, day, hour, minute, second);
|
||||
return time.AddTicks(nanoseconds / 100);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="T:System.DateTime" /> value to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime value to convert.</param>
|
||||
/// <returns>A byte array containing the S7 DateTimeLong representation of <paramref name="dateTime" />.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when the value of
|
||||
/// <paramref name="dateTime" /> is before <see cref="P:SpecMinimumDateTime" />
|
||||
/// or after <see cref="P:SpecMaximumDateTime" />.
|
||||
/// </exception>
|
||||
public static byte[] ToByteArray(System.DateTime dateTime)
|
||||
{
|
||||
if (dateTime < SpecMinimumDateTime)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
|
||||
$"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 DateTimeLong representation.");
|
||||
}
|
||||
|
||||
if (dateTime > SpecMaximumDateTime)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
|
||||
$"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 DateTimeLong representation.");
|
||||
}
|
||||
|
||||
var stream = new MemoryStream(TypeLengthInBytes);
|
||||
// Convert Year
|
||||
stream.Write(Word.ToByteArray(Convert.ToUInt16(dateTime.Year)), 0, 2);
|
||||
|
||||
// Convert Month
|
||||
stream.WriteByte(Convert.ToByte(dateTime.Month));
|
||||
|
||||
// Convert Day
|
||||
stream.WriteByte(Convert.ToByte(dateTime.Day));
|
||||
|
||||
// Convert WeekDay. NET DateTime starts with Sunday = 0, while S7DT has Sunday = 1.
|
||||
stream.WriteByte(Convert.ToByte(dateTime.DayOfWeek + 1));
|
||||
|
||||
// Convert Hour
|
||||
stream.WriteByte(Convert.ToByte(dateTime.Hour));
|
||||
|
||||
// Convert Minutes
|
||||
stream.WriteByte(Convert.ToByte(dateTime.Minute));
|
||||
|
||||
// Convert Seconds
|
||||
stream.WriteByte(Convert.ToByte(dateTime.Second));
|
||||
|
||||
// Convert Nanoseconds. Net DateTime has a representation of 1 Tick = 100ns.
|
||||
// Thus First take the ticks Mod 1 Second (1s = 10'000'000 ticks), and then Convert to nanoseconds.
|
||||
stream.Write(DWord.ToByteArray(Convert.ToUInt32(dateTime.Ticks % 10000000 * 100)), 0, 4);
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of <see cref="T:System.DateTime" /> values to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="dateTimes">The DateTime values to convert.</param>
|
||||
/// <returns>A byte array containing the S7 DateTimeLong representations of <paramref name="dateTimes" />.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when any value of
|
||||
/// <paramref name="dateTimes" /> is before <see cref="P:SpecMinimumDateTime" />
|
||||
/// or after <see cref="P:SpecMaximumDateTime" />.
|
||||
/// </exception>
|
||||
public static byte[] ToByteArray(System.DateTime[] dateTimes)
|
||||
{
|
||||
var bytes = new List<byte>(dateTimes.Length * TypeLengthInBytes);
|
||||
foreach (var dateTime in dateTimes)
|
||||
{
|
||||
bytes.AddRange(ToByteArray(dateTime));
|
||||
}
|
||||
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
private static T AssertRangeInclusive<T>(T input, T min, T max, string field) where T : IComparable<T>
|
||||
{
|
||||
if (input.CompareTo(min) < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(input), input,
|
||||
$"Value '{input}' is lower than the minimum '{min}' allowed for {field}.");
|
||||
}
|
||||
|
||||
if (input.CompareTo(max) > 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(input), input,
|
||||
$"Value '{input}' is higher than the maximum '{max}' allowed for {field}.");
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user