mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 22:38:27 +08:00
Merge branch 'release/0.3'
This commit is contained in:
17
GitVersion.yml
Normal file
17
GitVersion.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
assembly-informational-format: '{NuGetVersion}'
|
||||
mode: ContinuousDeployment
|
||||
branches:
|
||||
master:
|
||||
tag: rc
|
||||
increment: Minor
|
||||
features?[/-]:
|
||||
tag: rc-{BranchName}
|
||||
increment: Minor
|
||||
(pull|pull\-requests|pr)[/-]:
|
||||
tag: rc-pr-{BranchName}
|
||||
increment: Minor
|
||||
hotfix(es)?[/-]:
|
||||
tag: rc
|
||||
increment: Patch
|
||||
dev(elop)?(ment)?$:
|
||||
tag: b
|
||||
@@ -75,7 +75,7 @@ namespace S7.Net.UnitTest
|
||||
destTsap1, destTsap2, //Destination TASP
|
||||
192, //Parameter Code (tpdu-size)
|
||||
1, //Parameter Length
|
||||
9 //TPDU Size (2^9 = 512)
|
||||
11 //TPDU Size (2^11 = 2048)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@
|
||||
<Compile Include="S7NetTestsSync.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Helpers\TestLongStruct.cs" />
|
||||
<Compile Include="TypeTests\StringExTests.cs" />
|
||||
<Compile Include="TypeTests\StringTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="snap7.dll">
|
||||
|
||||
@@ -226,10 +226,8 @@ namespace S7.Net.UnitTest
|
||||
IntVariable111 = 201
|
||||
};
|
||||
plc.WriteStruct(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
TestLongStruct tc2 = (TestLongStruct)await plc.ReadStructAsync(typeof(TestLongStruct), DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
|
||||
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
|
||||
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
|
||||
@@ -292,11 +290,9 @@ namespace S7.Net.UnitTest
|
||||
IntVariable111 = 201
|
||||
};
|
||||
await plc.WriteClassAsync(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
TestLongClass tc2 = new TestLongClass();
|
||||
await plc.ReadClassAsync(tc2, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
|
||||
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
|
||||
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
|
||||
@@ -571,7 +567,7 @@ namespace S7.Net.UnitTest
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NullReferenceException))]
|
||||
public async Task Test_Async_ReadBytesThrowsExceptionIfPlcIsNotConnected()
|
||||
public async Task Test_Async_ReadBytesReturnsNullIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
@@ -831,11 +827,9 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
double test_value = 55.66;
|
||||
await plc.WriteAsync("DB1.DBD0", test_value);
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Write Double");
|
||||
var helper = await plc.ReadAsync("DB1.DBD0");
|
||||
double test_value2 = Conversion.ConvertToDouble((uint)helper);
|
||||
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Read Double");
|
||||
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
|
||||
}
|
||||
|
||||
@@ -844,11 +838,9 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
float test_value = 55.6632f;
|
||||
await plc.WriteAsync("DB1.DBD0", test_value);
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Write Single");
|
||||
var helper = await plc.ReadAsync("DB1.DBD0");
|
||||
float test_value2 = Conversion.ConvertToFloat((uint)helper);
|
||||
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Read Single");
|
||||
Assert.AreEqual(test_value, test_value2, "Compare Write/Read"); //No delta, datatype matches
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,14 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
if (plc.IsConnected == false)
|
||||
{
|
||||
var error = plc.Open();
|
||||
Assert.AreEqual(ErrorCode.NoError, error, "If you have s7 installed you must close s7oiehsx64 service.");
|
||||
try
|
||||
{
|
||||
plc.Open();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("If you have s7 installed you must close s7oiehsx64 service.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,10 +263,8 @@ namespace S7.Net.UnitTest
|
||||
tc.IntVariable110 = 200;
|
||||
tc.IntVariable111 = 201;
|
||||
plc.WriteStruct(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
TestLongStruct tc2 = (TestLongStruct)plc.ReadStruct(typeof(TestLongStruct), DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
|
||||
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
|
||||
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
|
||||
@@ -321,11 +325,9 @@ namespace S7.Net.UnitTest
|
||||
tc.IntVariable110 = 200;
|
||||
tc.IntVariable111 = 201;
|
||||
plc.WriteClass(tc, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
// Values that are read from a struct are stored in a new struct, returned by the funcion ReadStruct
|
||||
TestLongClass tc2 = new TestLongClass();
|
||||
plc.ReadClass(tc2, DB2);
|
||||
Assert.AreEqual(ErrorCode.NoError, plc.LastErrorCode);
|
||||
Assert.AreEqual(tc.IntVariable0, tc2.IntVariable0);
|
||||
Assert.AreEqual(tc.IntVariable1, tc2.IntVariable1);
|
||||
Assert.AreEqual(tc.IntVariable10, tc2.IntVariable10);
|
||||
@@ -596,19 +598,14 @@ namespace S7.Net.UnitTest
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void T13_ReadBytesReturnsEmptyArrayIfPlcIsNotConnected()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T13_ReadBytesThrowsIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
Assert.IsFalse(notConnectedPlc.IsConnected);
|
||||
|
||||
int expectedReadBytes = 0; // 0 bytes, because no connection was established
|
||||
|
||||
TestClass tc = new TestClass();
|
||||
int actualReadBytes = notConnectedPlc.ReadClass(tc, DB2);
|
||||
|
||||
Assert.AreEqual(expectedReadBytes, actualReadBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,8 +639,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(tc2.DWordVariable, tc2Generic.DWordVariable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T15_ReadClassWithGenericReturnsNullIfPlcIsNotConnected()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T15_ReadClassWithGenericThrowsIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
@@ -684,8 +681,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(tc2Generic.DWordVariable, tc2GenericWithClassFactory.DWordVariable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T17_ReadClassWithGenericAndClassFactoryReturnsNullIfPlcIsNotConnected()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T17_ReadClassWithGenericAndClassFactoryThrowsIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
@@ -697,8 +694,8 @@ namespace S7.Net.UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T18_ReadStructReturnsNullIfPlcIsNotConnected()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T18_ReadStructThrowsIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
@@ -739,8 +736,8 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(ts2.DWordVariable, ts2Generic.DWordVariable);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T20_ReadStructWithGenericReturnsNullIfPlcIsNotConnected()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T20_ReadStructThrowsIfPlcIsNotConnected()
|
||||
{
|
||||
using (var notConnectedPlc = new Plc(CpuType.S7300, "255.255.255.255", 0, 0))
|
||||
{
|
||||
@@ -883,11 +880,9 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
double test_value = 55.66;
|
||||
plc.Write("DB1.DBD0", test_value);
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Write Double");
|
||||
var helper = plc.Read("DB1.DBD0");
|
||||
double test_value2 = Conversion.ConvertToDouble((uint)helper);
|
||||
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Read Double");
|
||||
Assert.AreEqual(test_value, test_value2, 0.01, "Compare Write/Read"); //Need delta here because S7 only has 32 bit reals
|
||||
}
|
||||
|
||||
@@ -929,19 +924,17 @@ namespace S7.Net.UnitTest
|
||||
Assert.AreEqual(tc.Bool1, tc2.Bool1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void T29_Read_Write_ExceptionHandlingWhenPlcIsNotReachable()
|
||||
[TestMethod, ExpectedException(typeof(PlcException))]
|
||||
public void T29_Read_Write_ThrowsWhenPlcIsNotReachable()
|
||||
{
|
||||
// leave plc Open
|
||||
S7TestServer.Stop();
|
||||
|
||||
double test_value = 55.66;
|
||||
plc.Write("DB1.DBD0", test_value);
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.WriteData, "No Write Error.");
|
||||
|
||||
var helper = plc.Read("DB1.DBD0");
|
||||
Assert.AreEqual(helper, null, "Value in Read.");
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.ReadData, "No Read Error.");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -949,11 +942,9 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
float test_value = 55.6632f;
|
||||
plc.Write("DB1.DBD0", test_value);
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Write Single");
|
||||
var helper = plc.Read("DB1.DBD0");
|
||||
float test_value2 = Conversion.ConvertToFloat((uint)helper);
|
||||
|
||||
Assert.AreEqual(plc.LastErrorCode, ErrorCode.NoError, "Read Single");
|
||||
Assert.AreEqual(test_value, test_value2, "Compare Write/Read"); //No delta, datatype matches
|
||||
}
|
||||
|
||||
|
||||
115
S7.Net.UnitTest/TypeTests/StringExTests.cs
Normal file
115
S7.Net.UnitTest/TypeTests/StringExTests.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net.Types;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class StringExTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithZeroByteLength()
|
||||
{
|
||||
AssertFromByteArrayEquals("", 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithOneByteLength()
|
||||
{
|
||||
AssertFromByteArrayEquals("", 1, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithOneByteGarbage()
|
||||
{
|
||||
AssertFromByteArrayEquals("", 1, 0, (byte) 'a');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadA()
|
||||
{
|
||||
AssertFromByteArrayEquals("A", 1, 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadAbc()
|
||||
{
|
||||
AssertFromByteArrayEquals("Abc", 1, 3, (byte) 'A', (byte) 'b', (byte) 'c');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteNullWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals(null, 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals("", 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 0, 0, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteNullWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals(null, 1, 1, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("", 1, 1, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 1, 1, 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthTwo()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 2, 2, 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 1, 1, 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthTwo()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 2, 2, 2, (byte) 'A', (byte) 'b');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthThree()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 3, 3, 3, (byte) 'A', (byte) 'b', (byte) 'c');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthFour()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 4, 4, 3, (byte) 'A', (byte) 'b', (byte) 'c');
|
||||
}
|
||||
|
||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||
{
|
||||
Assert.AreEqual(expected, StringEx.FromByteArray(bytes));
|
||||
}
|
||||
|
||||
private static void AssertToByteArrayEquals(string value, int reservedLength, params byte[] expected)
|
||||
{
|
||||
CollectionAssert.AreEqual(expected, StringEx.ToByteArray(value, reservedLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
85
S7.Net.UnitTest/TypeTests/StringTests.cs
Normal file
85
S7.Net.UnitTest/TypeTests/StringTests.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net.Types;
|
||||
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class StringTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void WriteNullWIthReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals(null, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals("", 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthZero()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteNullWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals(null, 1, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteEmptyStringWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("", 1, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAWithReservedLengthTwo()
|
||||
{
|
||||
AssertToByteArrayEquals("A", 2, (byte) 'A', 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthOne()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 1, (byte) 'A');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthTwo()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 2, (byte) 'A', (byte) 'b');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthThree()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 3, (byte) 'A', (byte) 'b', (byte) 'c');
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WriteAbcWithReservedLengthFour()
|
||||
{
|
||||
AssertToByteArrayEquals("Abc", 4, (byte) 'A', (byte) 'b', (byte) 'c', 0);
|
||||
}
|
||||
|
||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||
{
|
||||
Assert.AreEqual(expected, String.FromByteArray(bytes));
|
||||
}
|
||||
|
||||
private static void AssertToByteArrayEquals(string value, int reservedLength, params byte[] expected)
|
||||
{
|
||||
CollectionAssert.AreEqual(expected, String.ToByteArray(value, reservedLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,15 @@ namespace S7.Net
|
||||
//TODO: Fix This
|
||||
get
|
||||
{
|
||||
return Connect() == ErrorCode.NoError;
|
||||
try
|
||||
{
|
||||
Connect();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,16 +80,6 @@ namespace S7.Net
|
||||
catch { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the last error registered when executing a function
|
||||
/// </summary>
|
||||
public string LastErrorString { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the last error code registered when executing a function
|
||||
/// </summary>
|
||||
public ErrorCode LastErrorCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a PLC object with all the parameters needed for connections.
|
||||
|
||||
197
S7.Net/PLCAddress.cs
Normal file
197
S7.Net/PLCAddress.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
namespace S7.Net
|
||||
{
|
||||
internal class PLCAddress
|
||||
{
|
||||
private DataType dataType;
|
||||
private int dbNumber;
|
||||
private int startByte;
|
||||
private int bitNumber;
|
||||
private VarType varType;
|
||||
|
||||
public DataType DataType
|
||||
{
|
||||
get => dataType;
|
||||
set => dataType = value;
|
||||
}
|
||||
|
||||
public int DbNumber
|
||||
{
|
||||
get => dbNumber;
|
||||
set => dbNumber = value;
|
||||
}
|
||||
|
||||
public int StartByte
|
||||
{
|
||||
get => startByte;
|
||||
set => startByte = value;
|
||||
}
|
||||
|
||||
public int BitNumber
|
||||
{
|
||||
get => bitNumber;
|
||||
set => bitNumber = value;
|
||||
}
|
||||
|
||||
public VarType VarType
|
||||
{
|
||||
get => varType;
|
||||
set => varType = value;
|
||||
}
|
||||
|
||||
public PLCAddress(string address)
|
||||
{
|
||||
Parse(address, out dataType, out dbNumber, out varType, out startByte, out bitNumber);
|
||||
}
|
||||
|
||||
public static void Parse(string input, out DataType dataType, out int dbNumber, out VarType varType, out int address, out int bitNumber)
|
||||
{
|
||||
bitNumber = -1;
|
||||
dbNumber = 0;
|
||||
|
||||
switch (input.Substring(0, 2))
|
||||
{
|
||||
case "DB":
|
||||
string[] strings = input.Split(new char[] { '.' });
|
||||
if (strings.Length < 2)
|
||||
throw new InvalidAddressException("To few periods for DB address");
|
||||
|
||||
dataType = DataType.DataBlock;
|
||||
dbNumber = int.Parse(strings[0].Substring(2));
|
||||
address = int.Parse(strings[1].Substring(3));
|
||||
|
||||
string dbType = strings[1].Substring(0, 3);
|
||||
switch (dbType)
|
||||
{
|
||||
case "DBB":
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "DBW":
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "DBD":
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "DBX":
|
||||
bitNumber = int.Parse(strings[2]);
|
||||
if (bitNumber > 7)
|
||||
throw new InvalidAddressException("Bit can only be 0-7");
|
||||
varType = VarType.Bit;
|
||||
return;
|
||||
default:
|
||||
throw new InvalidAddressException();
|
||||
}
|
||||
case "EB":
|
||||
// Input byte
|
||||
dataType = DataType.Input;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "EW":
|
||||
// Input word
|
||||
dataType = DataType.Input;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "ED":
|
||||
// Input double-word
|
||||
dataType = DataType.Input;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "AB":
|
||||
// Output byte
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "AW":
|
||||
// Output word
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "AD":
|
||||
// Output double-word
|
||||
dataType = DataType.Output;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "MB":
|
||||
// Memory byte
|
||||
dataType = DataType.Memory;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "MW":
|
||||
// Memory word
|
||||
dataType = DataType.Memory;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "MD":
|
||||
// Memory double-word
|
||||
dataType = DataType.Memory;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
default:
|
||||
switch (input.Substring(0, 1))
|
||||
{
|
||||
case "E":
|
||||
case "I":
|
||||
// Input
|
||||
dataType = DataType.Input;
|
||||
varType = VarType.Bit;
|
||||
break;
|
||||
case "A":
|
||||
case "O":
|
||||
// Output
|
||||
dataType = DataType.Output;
|
||||
varType = VarType.Bit;
|
||||
break;
|
||||
case "M":
|
||||
// Memory
|
||||
dataType = DataType.Memory;
|
||||
varType = VarType.Byte;
|
||||
break;
|
||||
case "T":
|
||||
// Timer
|
||||
dataType = DataType.Timer;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(1));
|
||||
varType = VarType.Timer;
|
||||
return;
|
||||
case "Z":
|
||||
case "C":
|
||||
// Counter
|
||||
dataType = DataType.Timer;
|
||||
dbNumber = 0;
|
||||
address = int.Parse(input.Substring(1));
|
||||
varType = VarType.Counter;
|
||||
return;
|
||||
default:
|
||||
throw new InvalidAddressException(string.Format("{0} is not a valid address", input.Substring(0, 1)));
|
||||
}
|
||||
|
||||
string txt2 = input.Substring(1);
|
||||
if (txt2.IndexOf(".") == -1)
|
||||
throw new InvalidAddressException("To few periods for DB address");
|
||||
|
||||
address = int.Parse(txt2.Substring(0, txt2.IndexOf(".")));
|
||||
bitNumber = int.Parse(txt2.Substring(txt2.IndexOf(".") + 1));
|
||||
if (bitNumber > 7)
|
||||
throw new InvalidAddressException("Bit can only be 0-7");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,167 +5,6 @@ using System.Linq;
|
||||
|
||||
namespace S7.Net
|
||||
{
|
||||
|
||||
internal class PLCAddress
|
||||
{
|
||||
public DataType dataType;
|
||||
public int DBNumber;
|
||||
public int Address;
|
||||
public int BitNumber;
|
||||
public VarType varType;
|
||||
|
||||
public PLCAddress(string address)
|
||||
{
|
||||
ParseString(address);
|
||||
}
|
||||
|
||||
private void ParseString(string address)
|
||||
{
|
||||
BitNumber = -1;
|
||||
switch (address.Substring(0, 2))
|
||||
{
|
||||
case "DB":
|
||||
string[] strings = address.Split(new char[] { '.' });
|
||||
if (strings.Length < 2)
|
||||
throw new InvalidAddressException("To few periods for DB address");
|
||||
|
||||
dataType = DataType.DataBlock;
|
||||
DBNumber = int.Parse(strings[0].Substring(2));
|
||||
Address = int.Parse(strings[1].Substring(3));
|
||||
|
||||
string dbType = strings[1].Substring(0, 3);
|
||||
switch (dbType)
|
||||
{
|
||||
case "DBB":
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "DBW":
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "DBD":
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "DBX":
|
||||
BitNumber = int.Parse(strings[2]);
|
||||
if (BitNumber > 7)
|
||||
throw new InvalidAddressException("Bit can only be 0-7");
|
||||
varType = VarType.Bit;
|
||||
return;
|
||||
default:
|
||||
throw new InvalidAddressException();
|
||||
}
|
||||
case "EB":
|
||||
// Input byte
|
||||
dataType = DataType.Input;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "EW":
|
||||
// Input word
|
||||
dataType = DataType.Input;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "ED":
|
||||
// Input double-word
|
||||
dataType = DataType.Input;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "AB":
|
||||
// Output byte
|
||||
dataType = DataType.Output;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "AW":
|
||||
// Output word
|
||||
dataType = DataType.Output;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "AD":
|
||||
// Output double-word
|
||||
dataType = DataType.Output;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
case "MB":
|
||||
// Memory byte
|
||||
dataType = DataType.Memory;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Byte;
|
||||
return;
|
||||
case "MW":
|
||||
// Memory word
|
||||
dataType = DataType.Memory;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.Word;
|
||||
return;
|
||||
case "MD":
|
||||
// Memory double-word
|
||||
dataType = DataType.Memory;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(2));
|
||||
varType = VarType.DWord;
|
||||
return;
|
||||
default:
|
||||
switch (address.Substring(0, 1))
|
||||
{
|
||||
case "E":
|
||||
case "I":
|
||||
// Input
|
||||
dataType = DataType.Input;
|
||||
break;
|
||||
case "A":
|
||||
case "O":
|
||||
// Output
|
||||
dataType = DataType.Output;
|
||||
break;
|
||||
case "M":
|
||||
// Memory
|
||||
dataType = DataType.Memory;
|
||||
break;
|
||||
case "T":
|
||||
// Timer
|
||||
dataType = DataType.Timer;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(1));
|
||||
varType = VarType.Timer;
|
||||
return;
|
||||
case "Z":
|
||||
case "C":
|
||||
// Counter
|
||||
dataType = DataType.Timer;
|
||||
DBNumber = 0;
|
||||
Address = int.Parse(address.Substring(1));
|
||||
varType = VarType.Counter;
|
||||
return;
|
||||
default:
|
||||
throw new InvalidAddressException(string.Format("{0} is not a valid address", address.Substring(0, 1)));
|
||||
}
|
||||
|
||||
string txt2 = address.Substring(1);
|
||||
if (txt2.IndexOf(".") == -1)
|
||||
throw new InvalidAddressException("To few periods for DB address");
|
||||
|
||||
Address = int.Parse(txt2.Substring(0, txt2.IndexOf(".")));
|
||||
BitNumber = int.Parse(txt2.Substring(txt2.IndexOf(".") + 1));
|
||||
if (BitNumber > 7)
|
||||
throw new InvalidAddressException("Bit can only be 0-7");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Plc
|
||||
{
|
||||
/// <summary>
|
||||
@@ -312,15 +151,6 @@ namespace S7.Net
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="LastErrorCode"/> to <see cref="ErrorCode.NoError"/> and <see cref="LastErrorString"/> to <see cref="string.Empty"/>.
|
||||
/// </summary>
|
||||
public void ClearLastError()
|
||||
{
|
||||
LastErrorCode = ErrorCode.NoError;
|
||||
LastErrorString = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a S7 <see cref="VarType"/> (Bool, Word, DWord, etc.), it returns how many bytes to read.
|
||||
/// </summary>
|
||||
@@ -367,7 +197,7 @@ namespace S7.Net
|
||||
{
|
||||
// check for Return Code = Success
|
||||
if (s7data[offset] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
|
||||
// to Data bytes
|
||||
offset += 4;
|
||||
|
||||
@@ -14,10 +14,9 @@ namespace S7.Net
|
||||
public partial class Plc
|
||||
{
|
||||
/// <summary>
|
||||
/// Open a <see cref="Socket"/> and connects to the PLC, sending all the corrected package
|
||||
/// and returning if the connection was successful (<see cref="ErrorCode.NoError"/>) of it was wrong.
|
||||
/// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
|
||||
/// </summary>
|
||||
/// <returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
|
||||
/// <returns>A task that represents the asynchronous open operation.</returns>
|
||||
public async Task OpenAsync()
|
||||
{
|
||||
await ConnectAsync();
|
||||
@@ -100,7 +99,7 @@ namespace S7.Net
|
||||
public async Task<object> ReadAsync(string variable)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
return await ReadAsync(adr.dataType, adr.DBNumber, adr.Address, adr.varType, 1, (byte)adr.BitNumber);
|
||||
return await ReadAsync(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -227,19 +226,17 @@ namespace S7.Net
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream); //TODO use Async
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
|
||||
ParseDataIntoDataItems(s7data, dataItems);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = socketException.Message;
|
||||
throw new PlcException(ErrorCode.ReadData, socketException);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = exc.Message;
|
||||
throw new PlcException(ErrorCode.ReadData, exc);
|
||||
}
|
||||
return dataItems;
|
||||
}
|
||||
@@ -252,8 +249,8 @@ namespace S7.Net
|
||||
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
{
|
||||
int localIndex = 0;
|
||||
int count = value.Length;
|
||||
@@ -263,15 +260,10 @@ 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 = (int)Math.Min(count, 200);
|
||||
ErrorCode lastError = await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
|
||||
if (lastError != ErrorCode.NoError)
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
await WriteBytesWithASingleRequestAsync(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
|
||||
count -= maxToWrite;
|
||||
localIndex += maxToWrite;
|
||||
}
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -282,19 +274,13 @@ namespace S7.Net
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
|
||||
{
|
||||
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));
|
||||
|
||||
ErrorCode lastError = await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value);
|
||||
if (lastError != ErrorCode.NoError)
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
await WriteBitWithASingleRequestAsync(dataType, db, startByteAdr, bitAdr, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -305,13 +291,13 @@ namespace S7.Net
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteBitAsync(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
|
||||
{
|
||||
if (value < 0 || value > 1)
|
||||
throw new ArgumentException("Value must be 0 or 1", nameof(value));
|
||||
|
||||
return await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, value == 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -324,26 +310,29 @@ 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>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteAsync(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteAsync(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
|
||||
{
|
||||
if (bitAdr != -1)
|
||||
{
|
||||
//Must be writing a bit value as bitAdr is specified
|
||||
if (value is bool)
|
||||
{
|
||||
return await WriteBitAsync(dataType, db, startByteAdr, bitAdr, (bool)value);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, (bool) value);
|
||||
}
|
||||
else if (value is int intValue)
|
||||
{
|
||||
if (intValue < 0 || intValue > 7)
|
||||
throw new ArgumentOutOfRangeException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr), nameof(bitAdr));
|
||||
throw new ArgumentOutOfRangeException(
|
||||
string.Format(
|
||||
"Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid",
|
||||
bitAdr), nameof(bitAdr));
|
||||
|
||||
return await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1);
|
||||
await WriteBitAsync(dataType, db, startByteAdr, bitAdr, intValue == 1);
|
||||
}
|
||||
throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
|
||||
else throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
|
||||
}
|
||||
return await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value));
|
||||
else await WriteBytesAsync(dataType, db, startByteAdr, Serialization.SerializeValue(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -352,11 +341,11 @@ namespace S7.Net
|
||||
/// </summary>
|
||||
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
|
||||
/// <param name="value">Value to be written to the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteAsync(string variable, object value)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteAsync(string variable, object value)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
return await WriteAsync(adr.dataType, adr.DBNumber, adr.Address, value, adr.BitNumber);
|
||||
await WriteAsync(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -365,12 +354,11 @@ namespace S7.Net
|
||||
/// <param name="structValue">The struct to be written</param>
|
||||
/// <param name="db">Db address</param>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteStructAsync(object structValue, int db, int startByteAdr = 0)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteStructAsync(object structValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
var bytes = Struct.ToBytes(structValue).ToList();
|
||||
var errCode = await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
return errCode;
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -379,12 +367,11 @@ namespace S7.Net
|
||||
/// <param name="classValue">The class to be written</param>
|
||||
/// <param name="db">Db address</param>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public async Task<ErrorCode> WriteClassAsync(object classValue, int db, int startByteAdr = 0)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
public async Task WriteClassAsync(object classValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
var bytes = Types.Class.ToBytes(classValue).ToList();
|
||||
var errCode = await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
return errCode;
|
||||
await WriteBytesAsync(DataType.DataBlock, db, startByteAdr, bytes.ToArray());
|
||||
}
|
||||
|
||||
private async Task<byte[]> ReadBytesWithSingleRequestAsync(DataType dataType, int db, int startByteAdr, int count)
|
||||
@@ -402,7 +389,7 @@ namespace S7.Net
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
|
||||
for (int cnt = 0; cnt < count; cnt++)
|
||||
bytes[cnt] = s7data[cnt + 18];
|
||||
@@ -427,15 +414,14 @@ namespace S7.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes up to 200 bytes to the PLC and returns NoError if successful. You must specify the memory area type, memory are address, byte start address and bytes count.
|
||||
/// If the write was not successful, check LastErrorCode or LastErrorString.
|
||||
/// Writes up to 200 bytes to the PLC. You must specify the memory area type, memory are address, byte start address and bytes count.
|
||||
/// </summary>
|
||||
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
|
||||
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
|
||||
/// <param name="startByteAdr">Start byte address. If you want to read DB1.DBW200, this is 200.</param>
|
||||
/// <param name="value">Bytes to write. The lenght of this parameter can't be higher than 200. If you need more, use recursion.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
private async Task<ErrorCode> WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
/// <returns>A task that represents the asynchronous write operation.</returns>
|
||||
private async Task WriteBytesWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
{
|
||||
byte[] bReceive = new byte[513];
|
||||
int varCount = 0;
|
||||
@@ -471,27 +457,23 @@ namespace S7.Net
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
throw new PlcException(ErrorCode.WriteData, exc);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ErrorCode> WriteBitWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
|
||||
private async Task WriteBitWithASingleRequestAsync(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
|
||||
{
|
||||
byte[] bReceive = new byte[513];
|
||||
int varCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var value = new[] { bitValue ? (byte)1 : (byte)0 };
|
||||
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
|
||||
varCount = value.Length;
|
||||
// first create the header
|
||||
int packageSize = 35 + value.Length;
|
||||
@@ -520,15 +502,11 @@ namespace S7.Net
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
return ErrorCode.NoError;
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
throw new PlcException(ErrorCode.WriteData, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
S7.Net/PlcException.cs
Normal file
39
S7.Net/PlcException.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
|
||||
namespace S7.Net
|
||||
{
|
||||
#if NET_FULL
|
||||
[Serializable]
|
||||
#endif
|
||||
public class PlcException : Exception
|
||||
{
|
||||
public ErrorCode ErrorCode { get; }
|
||||
|
||||
public PlcException(ErrorCode errorCode) : this(errorCode, $"PLC communication failed with error '{errorCode}'.")
|
||||
{
|
||||
}
|
||||
|
||||
public PlcException(ErrorCode errorCode, Exception innerException) : this(errorCode, innerException.Message,
|
||||
innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public PlcException(ErrorCode errorCode, string message) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public PlcException(ErrorCode errorCode, string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
#if NET_FULL
|
||||
protected PlcException(System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
ErrorCode = (ErrorCode) info.GetInt32(nameof(ErrorCode));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -11,16 +11,12 @@ namespace S7.Net
|
||||
public partial class Plc
|
||||
{
|
||||
/// <summary>
|
||||
/// Open a <see cref="Socket"/> and connects to the PLC, sending all the corrected package
|
||||
/// and returning if the connection was successful (<see cref="ErrorCode.NoError"/>) of it was wrong.
|
||||
/// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
|
||||
/// </summary>
|
||||
/// <returns>Returns ErrorCode.NoError if the connection was successful, otherwise check the ErrorCode</returns>
|
||||
public ErrorCode Open()
|
||||
public void Open()
|
||||
{
|
||||
if (Connect() != ErrorCode.NoError)
|
||||
{
|
||||
return LastErrorCode;
|
||||
}
|
||||
Connect();
|
||||
|
||||
try
|
||||
{
|
||||
stream.Write(ConnectionRequest.GetCOTPConnectionRequest(CPU, Rack, Slot), 0, 22);
|
||||
@@ -41,14 +37,12 @@ namespace S7.Net
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ConnectionError;
|
||||
LastErrorString = string.Format("Couldn't establish the connection to {0}.\nMessage: {1}", IP, exc.Message);
|
||||
return ErrorCode.ConnectionError;
|
||||
throw new PlcException(ErrorCode.ConnectionError,
|
||||
$"Couldn't establish the connection to {IP}.\nMessage: {exc.Message}", exc);
|
||||
}
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
|
||||
private ErrorCode Connect()
|
||||
private void Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -59,23 +53,15 @@ namespace S7.Net
|
||||
catch (SocketException sex)
|
||||
{
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
|
||||
if (sex.SocketErrorCode == SocketError.TimedOut)
|
||||
{
|
||||
LastErrorCode = ErrorCode.IPAddressNotAvailable;
|
||||
}
|
||||
else
|
||||
{
|
||||
LastErrorCode = ErrorCode.ConnectionError;
|
||||
}
|
||||
|
||||
LastErrorString = sex.Message;
|
||||
throw new PlcException(
|
||||
sex.SocketErrorCode == SocketError.TimedOut
|
||||
? ErrorCode.IPAddressNotAvailable
|
||||
: ErrorCode.ConnectionError, sex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ConnectionError;
|
||||
LastErrorString = ex.Message;
|
||||
throw new PlcException(ErrorCode.ConnectionError, ex);
|
||||
}
|
||||
return LastErrorCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -133,7 +119,7 @@ namespace S7.Net
|
||||
public object Read(string variable)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
return Read(adr.dataType, adr.DBNumber, adr.Address, adr.varType, 1, (byte)adr.BitNumber);
|
||||
return Read(adr.DataType, adr.DbNumber, adr.StartByte, adr.VarType, 1, (byte)adr.BitNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -231,8 +217,7 @@ namespace S7.Net
|
||||
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
public void WriteBytes(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
{
|
||||
int localIndex = 0;
|
||||
int count = value.Length;
|
||||
@@ -241,16 +226,11 @@ namespace S7.Net
|
||||
//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);
|
||||
ErrorCode lastError = WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
|
||||
if (lastError != ErrorCode.NoError)
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
var maxToWrite = Math.Min(count, 200);
|
||||
WriteBytesWithASingleRequest(dataType, db, startByteAdr + localIndex, value.Skip(localIndex).Take(maxToWrite).ToArray());
|
||||
count -= maxToWrite;
|
||||
localIndex += maxToWrite;
|
||||
}
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -261,36 +241,28 @@ namespace S7.Net
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
|
||||
public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, bool value)
|
||||
{
|
||||
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));
|
||||
|
||||
ErrorCode lastError = WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value);
|
||||
if (lastError != ErrorCode.NoError)
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
WriteBitWithASingleRequest(dataType, db, startByteAdr, bitAdr, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single bit from a DB with the specified index.
|
||||
/// Write a single bit to a DB with the specified index.
|
||||
/// </summary>
|
||||
/// <param name="dataType">Data type of the memory area, can be DB, Timer, Counter, Merker(Memory), Input, Output.</param>
|
||||
/// <param name="db">Address of the memory area (if you want to read DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
|
||||
/// <param name="db">Address of the memory area (if you want to write DB1, this is set to 1). This must be set also for other memory area types: counters, timers,etc.</param>
|
||||
/// <param name="startByteAdr">Start byte address. If you want to write DB1.DBW200, this is 200.</param>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <param name="value">Bytes to write. If more than 200, multiple requests will be made.</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
|
||||
/// <param name="value">Value to write (0 or 1).</param>
|
||||
public void WriteBit(DataType dataType, int db, int startByteAdr, int bitAdr, int value)
|
||||
{
|
||||
if (value < 0 || value > 1)
|
||||
throw new ArgumentException("Value must be 0 or 1", nameof(value));
|
||||
|
||||
return WriteBit(dataType, db, startByteAdr, bitAdr, value == 1);
|
||||
WriteBit(dataType, db, startByteAdr, bitAdr, value == 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -303,26 +275,29 @@ 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>
|
||||
/// <param name="bitAdr">The address of the bit. (0-7)</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
|
||||
public void Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
|
||||
{
|
||||
if (bitAdr != -1)
|
||||
{
|
||||
//Must be writing a bit value as bitAdr is specified
|
||||
if (value is bool)
|
||||
{
|
||||
return WriteBit(dataType, db, startByteAdr, bitAdr, (bool)value);
|
||||
WriteBit(dataType, db, startByteAdr, bitAdr, (bool) value);
|
||||
}
|
||||
else if (value is int intValue)
|
||||
{
|
||||
if (intValue < 0 || intValue > 7)
|
||||
throw new ArgumentOutOfRangeException(string.Format("Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid", bitAdr), nameof(bitAdr));
|
||||
throw new ArgumentOutOfRangeException(
|
||||
string.Format(
|
||||
"Addressing Error: You can only reference bitwise locations 0-7. Address {0} is invalid",
|
||||
bitAdr), nameof(bitAdr));
|
||||
|
||||
return WriteBit(dataType, db, startByteAdr, bitAdr, intValue == 1);
|
||||
WriteBit(dataType, db, startByteAdr, bitAdr, intValue == 1);
|
||||
}
|
||||
throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
|
||||
else
|
||||
throw new ArgumentException("Value must be a bool or an int to write a bit", nameof(value));
|
||||
}
|
||||
return WriteBytes(dataType, db, startByteAdr, Serialization.SerializeValue(value));
|
||||
else WriteBytes(dataType, db, startByteAdr, Serialization.SerializeValue(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -331,11 +306,10 @@ namespace S7.Net
|
||||
/// </summary>
|
||||
/// <param name="variable">Input strings like "DB1.DBX0.0", "DB20.DBD200", "MB20", "T45", etc.</param>
|
||||
/// <param name="value">Value to be written to the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode Write(string variable, object value)
|
||||
public void Write(string variable, object value)
|
||||
{
|
||||
var adr = new PLCAddress(variable);
|
||||
return Write(adr.dataType, adr.DBNumber, adr.Address, value, adr.BitNumber);
|
||||
Write(adr.DataType, adr.DbNumber, adr.StartByte, value, adr.BitNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -344,10 +318,9 @@ namespace S7.Net
|
||||
/// <param name="structValue">The struct to be written</param>
|
||||
/// <param name="db">Db address</param>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteStruct(object structValue, int db, int startByteAdr = 0)
|
||||
public void WriteStruct(object structValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
return WriteStructAsync(structValue, db, startByteAdr).Result;
|
||||
WriteStructAsync(structValue, db, startByteAdr).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -356,10 +329,9 @@ namespace S7.Net
|
||||
/// <param name="classValue">The class to be written</param>
|
||||
/// <param name="db">Db address</param>
|
||||
/// <param name="startByteAdr">Start bytes on the PLC</param>
|
||||
/// <returns>NoError if it was successful, or the error is specified</returns>
|
||||
public ErrorCode WriteClass(object classValue, int db, int startByteAdr = 0)
|
||||
public void WriteClass(object classValue, int db, int startByteAdr = 0)
|
||||
{
|
||||
return WriteClassAsync(classValue, db, startByteAdr).Result;
|
||||
WriteClassAsync(classValue, db, startByteAdr).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private byte[] ReadBytesWithSingleRequest(DataType dataType, int db, int startByteAdr, int count)
|
||||
@@ -378,24 +350,16 @@ namespace S7.Net
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
|
||||
for (int cnt = 0; cnt < count; cnt++)
|
||||
bytes[cnt] = s7data[cnt + 18];
|
||||
|
||||
return bytes;
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = socketException.Message;
|
||||
return null;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = exc.Message;
|
||||
return null;
|
||||
throw new PlcException(ErrorCode.ReadData, exc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +378,7 @@ namespace S7.Net
|
||||
S7WriteMultiple.ParseResponse(response, response.Length, dataItems);
|
||||
}
|
||||
|
||||
private ErrorCode WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
private void WriteBytesWithASingleRequest(DataType dataType, int db, int startByteAdr, byte[] value)
|
||||
{
|
||||
int varCount = 0;
|
||||
try
|
||||
@@ -448,26 +412,22 @@ namespace S7.Net
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
|
||||
return ErrorCode.NoError;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
throw new PlcException(ErrorCode.WriteData, exc);
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorCode WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
|
||||
private void WriteBitWithASingleRequest(DataType dataType, int db, int startByteAdr, int bitAdr, bool bitValue)
|
||||
{
|
||||
int varCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var value = new[] { bitValue ? (byte)1 : (byte)0 };
|
||||
var value = new[] {bitValue ? (byte) 1 : (byte) 0};
|
||||
varCount = value.Length;
|
||||
// first create the header
|
||||
int packageSize = 35 + value.Length;
|
||||
@@ -496,15 +456,11 @@ namespace S7.Net
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
|
||||
return ErrorCode.NoError;
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.WriteData;
|
||||
LastErrorString = exc.Message;
|
||||
return LastErrorCode;
|
||||
throw new PlcException(ErrorCode.WriteData, exc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,7 +487,7 @@ namespace S7.Net
|
||||
{
|
||||
// first create the header
|
||||
int packageSize = 19 + (dataItems.Count * 12);
|
||||
Types.ByteArray package = new ByteArray(packageSize);
|
||||
ByteArray package = new ByteArray(packageSize);
|
||||
package.Add(ReadHeaderPackage(dataItems.Count));
|
||||
// package.Add(0x02); // datenart
|
||||
foreach (var dataItem in dataItems)
|
||||
@@ -543,19 +499,13 @@ namespace S7.Net
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream); //TODO use Async
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new Exception(ErrorCode.WrongNumberReceivedBytes.ToString());
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
|
||||
ParseDataIntoDataItems(s7data, dataItems);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = socketException.Message;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
LastErrorCode = ErrorCode.ReadData;
|
||||
LastErrorString = exc.Message;
|
||||
throw new PlcException(ErrorCode.ReadData, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace S7.Net.Protocol
|
||||
3, 0, //Destination TASP
|
||||
192, //Parameter Code (tpdu-size)
|
||||
1, //Parameter Length
|
||||
9 //TPDU Size (2^9 = 512)
|
||||
11 //TPDU Size (2^11 = 2048)
|
||||
};
|
||||
|
||||
switch (cpu)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace S7.Net.Protocol
|
||||
foreach (var item in dataItems)
|
||||
{
|
||||
message.Add(Parameter.Template);
|
||||
var value = Serialization.SerializeValue(item.Value);
|
||||
var value = Serialization.SerializeDataItem(item);
|
||||
var wordLen = item.Value is bool ? 1 : 2;
|
||||
|
||||
message[paramOffset + Parameter.Offsets.WordLength] = (byte) wordLen;
|
||||
|
||||
@@ -11,6 +11,16 @@ namespace S7.Net.Protocol
|
||||
return (ushort)((buf[index] << 8) + buf[index]);
|
||||
}
|
||||
|
||||
public static byte[] SerializeDataItem(DataItem dataItem)
|
||||
{
|
||||
if (dataItem.Value is string s)
|
||||
return dataItem.VarType == VarType.StringEx
|
||||
? StringEx.ToByteArray(s, dataItem.Count)
|
||||
: Types.String.ToByteArray(s, dataItem.Count);
|
||||
|
||||
return SerializeValue(dataItem.Value);
|
||||
}
|
||||
|
||||
public static byte[] SerializeValue(object value)
|
||||
{
|
||||
switch (value.GetType().Name)
|
||||
@@ -46,7 +56,10 @@ namespace S7.Net.Protocol
|
||||
case "Single[]":
|
||||
return Types.Single.ToByteArray((float[])value);
|
||||
case "String":
|
||||
return Types.String.ToByteArray(value as 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;
|
||||
return Types.String.ToByteArray(stringVal, stringVal.Length);
|
||||
default:
|
||||
throw new InvalidVariableTypeException();
|
||||
}
|
||||
|
||||
@@ -23,5 +23,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-62925-02" PrivateAssets="All" />
|
||||
<PackageReference Include="SourceLink.Copy.PdbFiles" Version="2.8.3" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace S7.Net.Types
|
||||
using System;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an instance of a memory block that can be read by using ReadMultipleVars
|
||||
@@ -48,5 +50,42 @@
|
||||
VarType = VarType.Byte;
|
||||
Count = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="DataItem"/> from the supplied address.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to create the DataItem for.</param>
|
||||
/// <returns>A new <see cref="DataItem"/> instance with properties parsed from <paramref name="address"/>.</returns>
|
||||
/// <remarks>The <see cref="Count" /> property is not parsed from the address.</remarks>
|
||||
public static DataItem FromAddress(string address)
|
||||
{
|
||||
PLCAddress.Parse(address, out var dataType, out var dbNumber, out var varType, out var startByte,
|
||||
out var bitNumber);
|
||||
|
||||
return new DataItem
|
||||
{
|
||||
DataType = dataType,
|
||||
DB = dbNumber,
|
||||
VarType = varType,
|
||||
StartByteAdr = startByte,
|
||||
BitAdr = (byte) bitNumber
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance of <see cref="DataItem"/> from the supplied address and value.
|
||||
/// </summary>
|
||||
/// <param name="address">The address to create the DataItem for.</param>
|
||||
/// <param name="value">The value to be applied to the DataItem.</param>
|
||||
/// <returns>A new <see cref="DataItem"/> instance with properties parsed from <paramref name="address"/> and the supplied value set.</returns>
|
||||
public static DataItem FromAddressAndValue<T>(string address, T value)
|
||||
{
|
||||
var dataItem = FromAddress(address);
|
||||
dataItem.Value = value;
|
||||
|
||||
if (typeof(T).IsArray) dataItem.Count = ((Array) dataItem.Value).Length;
|
||||
|
||||
return dataItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,21 @@
|
||||
public class String
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a string to S7 bytes
|
||||
/// Converts a string to <paramref name="reservedLength"/> of bytes, padded with 0-bytes if required.
|
||||
/// </summary>
|
||||
public static byte[] ToByteArray(string value)
|
||||
/// <param name="value">The string to write to the PLC.</param>
|
||||
/// <param name="reservedLength">The amount of bytes reserved for the <paramref name="value"/> in the PLC.</param>
|
||||
public static byte[] ToByteArray(string value, int reservedLength)
|
||||
{
|
||||
return System.Text.Encoding.ASCII.GetBytes(value);
|
||||
var length = value?.Length;
|
||||
if (length > reservedLength) length = reservedLength;
|
||||
var bytes = new byte[reservedLength];
|
||||
|
||||
if (length == null || length == 0) return bytes;
|
||||
|
||||
System.Text.Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 0);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace S7.Net.Types
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the methods to convert from S7 strings to C# strings
|
||||
@@ -21,6 +24,27 @@
|
||||
|
||||
return System.Text.Encoding.ASCII.GetString(bytes, 2, length);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
/// <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)
|
||||
{
|
||||
if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}.");
|
||||
|
||||
var length = value?.Length;
|
||||
if (length > reservedLength) length = reservedLength;
|
||||
|
||||
var bytes = new byte[(length ?? 0) + 2];
|
||||
bytes[0] = (byte) reservedLength;
|
||||
|
||||
if (value == null) return bytes;
|
||||
|
||||
bytes[1] = (byte) Encoding.ASCII.GetBytes(value, 0, length.Value, bytes, 2);
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
appveyor.yml
Normal file
13
appveyor.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
image: Visual Studio 2017
|
||||
configuration: Release
|
||||
install:
|
||||
- choco install gitversion.portable -y
|
||||
before_build:
|
||||
- cmd: gitversion /l console /output buildserver /b %APPVEYOR_REPO_BRANCH%
|
||||
- nuget restore
|
||||
build_script:
|
||||
msbuild /nologo /v:m /p:AssemblyVersion=%GitVersion_AssemblySemVer% /p:FileVersion=%GitVersion_MajorMinorPatch% /p:InformationalVersion=%GitVersion_InformationalVersion% /p:Configuration=%CONFIGURATION% S7.sln
|
||||
after_build:
|
||||
- dotnet pack S7.Net -c %CONFIGURATION% /p:Version=%GitVersion_NuGetVersion% --no-build -o ..\artifacts
|
||||
artifacts:
|
||||
- path: artifacts\*.*
|
||||
Reference in New Issue
Block a user