mirror of
https://github.com/S7NetPlus/s7netplus.git
synced 2026-02-17 14:28:25 +08:00
Merge branch 'develop' into patch-1
This commit is contained in:
@@ -34,6 +34,5 @@ PM> Install-Package S7netplus
|
|||||||
|
|
||||||
## Running the tests
|
## Running the tests
|
||||||
|
|
||||||
Unit tests use Snap7 server, so port 102 must be not in use.
|
Unit tests use Snap7 server.
|
||||||
If you have Siemens Step7 installed, the service s7oiehsx64 is stopped when running unit tests.
|
On Windows, the DLL is included with the test project. On other platforms, Snap7 must be installed manually before running tests.
|
||||||
You have to restart the service manually if you need it.
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace S7.Net.UnitTest.Helpers
|
|||||||
Console.WriteLine(Server.EventText(ref Event));
|
Console.WriteLine(Server.EventText(ref Event));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Start()
|
public static void Start(short port)
|
||||||
{
|
{
|
||||||
Server = new S7Server();
|
Server = new S7Server();
|
||||||
// Share some resources with our virtual PLC
|
// Share some resources with our virtual PLC
|
||||||
@@ -59,7 +59,14 @@ namespace S7.Net.UnitTest.Helpers
|
|||||||
// Start the server onto the default adapter.
|
// Start the server onto the default adapter.
|
||||||
// To select an adapter we have to use Server->StartTo("192.168.x.y").
|
// To select an adapter we have to use Server->StartTo("192.168.x.y").
|
||||||
// Start() is the same of StartTo("0.0.0.0");
|
// Start() is the same of StartTo("0.0.0.0");
|
||||||
|
|
||||||
|
Server.SetParam(S7Consts.p_u16_LocalPort, ref port);
|
||||||
|
|
||||||
int Error = Server.Start();
|
int Error = Server.Start();
|
||||||
|
if (Error != 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Error starting Snap7 server: {Server.ErrorText(Error)}");
|
||||||
|
}
|
||||||
//if (Error == 0)
|
//if (Error == 0)
|
||||||
//{
|
//{
|
||||||
// // Now the server is running ... wait a key to terminate
|
// // Now the server is running ... wait a key to terminate
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ using S7.Net;
|
|||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using S7.Net.Protocol;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace S7.Net.UnitTest
|
namespace S7.Net.UnitTest
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ProtocolUnitTest
|
public class ProtocolUnitTest
|
||||||
{
|
{
|
||||||
private TestContext TestContext { get; set; }
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TPKT_Read()
|
public async Task TPKT_Read()
|
||||||
@@ -64,6 +66,32 @@ namespace S7.Net.UnitTest
|
|||||||
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
|
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestResponseCode()
|
||||||
|
{
|
||||||
|
var expected = StringToByteArray("320700000400000800080001120411440100ff09000400000000");
|
||||||
|
var m = new MemoryStream(StringToByteArray("0300000702f0000300000702f0000300002102f080320700000400000800080001120411440100ff09000400000000"));
|
||||||
|
var t = COTP.TSDU.Read(m);
|
||||||
|
Assert.IsTrue(expected.SequenceEqual(t));
|
||||||
|
|
||||||
|
|
||||||
|
// Test all possible byte values. Everything except 0xff should throw an exception.
|
||||||
|
var testData = Enumerable.Range(0, 256).Select(i => new { StatusCode = (ReadWriteErrorCode)i, ThrowsException = i != (byte)ReadWriteErrorCode.Success });
|
||||||
|
|
||||||
|
foreach (var entry in testData)
|
||||||
|
{
|
||||||
|
if (entry.ThrowsException)
|
||||||
|
{
|
||||||
|
Assert.ThrowsException<Exception>(() => Plc.ValidateResponseCode(entry.StatusCode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Plc.ValidateResponseCode(entry.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net452</TargetFrameworks>
|
<TargetFrameworks>net452;netcoreapp3.1</TargetFrameworks>
|
||||||
|
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<Copyright>Copyright © 2014</Copyright>
|
<Copyright>Copyright © 2014</Copyright>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="GitHubActionsTestLogger" Version="1.1.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
@@ -24,13 +26,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System.ServiceProcess" />
|
<None Update="runtimes\win-x64\native\snap7.dll" Link="snap7.dll">
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="snap7.dll">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#region Using
|
#region Using
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using S7.Net;
|
|
||||||
using S7.Net.UnitTest.Helpers;
|
using S7.Net.UnitTest.Helpers;
|
||||||
using S7.Net.UnitTest;
|
|
||||||
using System.ServiceProcess;
|
|
||||||
using S7.Net.Types;
|
using S7.Net.Types;
|
||||||
using S7.UnitTest.Helpers;
|
using S7.UnitTest.Helpers;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#region Using
|
#region Using
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using S7.Net;
|
|
||||||
using S7.Net.UnitTest.Helpers;
|
using S7.Net.UnitTest.Helpers;
|
||||||
using S7.Net.UnitTest;
|
|
||||||
using System.ServiceProcess;
|
|
||||||
using S7.Net.Types;
|
using S7.Net.Types;
|
||||||
using S7.UnitTest.Helpers;
|
using S7.UnitTest.Helpers;
|
||||||
|
|
||||||
@@ -41,6 +37,8 @@ namespace S7.Net.UnitTest
|
|||||||
#region Constants
|
#region Constants
|
||||||
const int DB2 = 2;
|
const int DB2 = 2;
|
||||||
const int DB4 = 4;
|
const int DB4 = 4;
|
||||||
|
const short TestServerPort = 31122;
|
||||||
|
const string TestServerIp = "127.0.0.1";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private fields
|
#region Private fields
|
||||||
@@ -53,16 +51,19 @@ namespace S7.Net.UnitTest
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public S7NetTests()
|
public S7NetTests()
|
||||||
{
|
{
|
||||||
plc = new Plc(CpuType.S7300, "127.0.0.1", 0, 2);
|
plc = CreatePlc();
|
||||||
//ConsoleManager.Show();
|
|
||||||
ShutDownServiceS7oiehsx64();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Plc CreatePlc()
|
||||||
|
{
|
||||||
|
return new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
S7TestServer.Start();
|
S7TestServer.Start(TestServerPort);
|
||||||
plc.Open();
|
plc.Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,9 +932,9 @@ namespace S7.Net.UnitTest
|
|||||||
{
|
{
|
||||||
plc.Close();
|
plc.Close();
|
||||||
S7TestServer.Stop();
|
S7TestServer.Stop();
|
||||||
S7TestServer.Start();
|
S7TestServer.Start(TestServerPort);
|
||||||
|
|
||||||
var reachablePlc = new Plc(CpuType.S7300, "127.0.0.1", 0, 2);
|
var reachablePlc = CreatePlc();
|
||||||
Assert.IsTrue(reachablePlc.IsAvailable);
|
Assert.IsTrue(reachablePlc.IsAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1026,18 +1027,6 @@ namespace S7.Net.UnitTest
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private methods
|
#region Private methods
|
||||||
private static void ShutDownServiceS7oiehsx64()
|
|
||||||
{
|
|
||||||
ServiceController[] services = ServiceController.GetServices();
|
|
||||||
var service = services.FirstOrDefault(s => s.ServiceName == "s7oiehsx64");
|
|
||||||
if (service != null)
|
|
||||||
{
|
|
||||||
if (service.Status == ServiceControllerStatus.Running)
|
|
||||||
{
|
|
||||||
service.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable Support
|
#region IDisposable Support
|
||||||
private bool disposedValue = false; // To detect redundant calls
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|||||||
@@ -36,11 +36,8 @@ namespace Snap7
|
|||||||
|
|
||||||
public class S7Consts
|
public class S7Consts
|
||||||
{
|
{
|
||||||
#if __MonoCS__ // Assuming that we are using Unix release of Mono (otherwise modify it)
|
public const string Snap7LibName = "snap7";
|
||||||
public const string Snap7LibName = "libsnap7.so";
|
|
||||||
#else
|
|
||||||
public const string Snap7LibName = "snap7.dll";
|
|
||||||
#endif
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// PARAMS LIST
|
// PARAMS LIST
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace S7.Net.UnitTest
|
|||||||
[TestClass]
|
[TestClass]
|
||||||
public class StreamTests
|
public class StreamTests
|
||||||
{
|
{
|
||||||
private TestContext TestContext { get; set; }
|
public TestContext TestContext { get; set; }
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task TPKT_ReadRestrictedStreamAsync()
|
public async Task TPKT_ReadRestrictedStreamAsync()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Linq;
|
|||||||
namespace S7.Net.UnitTest.TypeTests
|
namespace S7.Net.UnitTest.TypeTests
|
||||||
{
|
{
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class StringExTests
|
public class S7StringTests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ReadEmptyStringWithZeroByteLength()
|
public void ReadEmptyStringWithZeroByteLength()
|
||||||
@@ -36,7 +36,7 @@ namespace S7.Net.UnitTest.TypeTests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void ReadMalformedStringSizeLargerThanCapacity()
|
public void ReadMalformedStringSizeLargerThanCapacity()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<PlcException>(() => StringEx.FromByteArray(new byte[] { 3, 5, 0, 1, 2 }));
|
Assert.ThrowsException<PlcException>(() => S7String.FromByteArray(new byte[] { 3, 5, 0, 1, 2 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -102,7 +102,7 @@ namespace S7.Net.UnitTest.TypeTests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void WriteAbcWithStringLargetThanReservedLength()
|
public void WriteAbcWithStringLargetThanReservedLength()
|
||||||
{
|
{
|
||||||
Assert.ThrowsException<ArgumentException>(() => StringEx.ToByteArray("Abc", 2));
|
Assert.ThrowsException<ArgumentException>(() => S7String.ToByteArray("Abc", 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@@ -119,16 +119,16 @@ namespace S7.Net.UnitTest.TypeTests
|
|||||||
|
|
||||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||||
{
|
{
|
||||||
var convertedString = StringEx.FromByteArray(bytes);
|
var convertedString = S7String.FromByteArray(bytes);
|
||||||
Assert.AreEqual(expected, convertedString);
|
Assert.AreEqual(expected, convertedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void AssertToByteArrayAndBackEquals(string value, int reservedLength, params byte[] expected)
|
private static void AssertToByteArrayAndBackEquals(string value, int reservedLength, params byte[] expected)
|
||||||
{
|
{
|
||||||
var convertedData = StringEx.ToByteArray(value, reservedLength);
|
var convertedData = S7String.ToByteArray(value, reservedLength);
|
||||||
CollectionAssert.AreEqual(expected, convertedData);
|
CollectionAssert.AreEqual(expected, convertedData);
|
||||||
var convertedBack = StringEx.FromByteArray(convertedData);
|
var convertedBack = S7String.FromByteArray(convertedData);
|
||||||
Assert.AreEqual(value, convertedBack);
|
Assert.AreEqual(value, convertedBack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
S7.Net.UnitTest/runtimes/win-x64/native/snap7.dll
Normal file
BIN
S7.Net.UnitTest/runtimes/win-x64/native/snap7.dll
Normal file
Binary file not shown.
Binary file not shown.
@@ -169,14 +169,14 @@
|
|||||||
LReal,
|
LReal,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String variable type (variable)
|
/// Char Array / C-String variable type (variable)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
String,
|
String,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// String variable type (variable)
|
/// S7 String variable type (variable)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
StringEx,
|
S7String,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Timer variable type
|
/// Timer variable type
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using S7.Net.Protocol;
|
||||||
using S7.Net.Types;
|
using S7.Net.Types;
|
||||||
|
|
||||||
|
|
||||||
@@ -18,8 +19,8 @@ namespace S7.Net
|
|||||||
private TcpClient? tcpClient;
|
private TcpClient? tcpClient;
|
||||||
private NetworkStream? _stream;
|
private NetworkStream? _stream;
|
||||||
|
|
||||||
private int readTimeout = System.Threading.Timeout.Infinite;
|
private int readTimeout = 0; // default no timeout
|
||||||
private int writeTimeout = System.Threading.Timeout.Infinite;
|
private int writeTimeout = 0; // default no timeout
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IP address of the PLC
|
/// IP address of the PLC
|
||||||
@@ -234,13 +235,34 @@ namespace S7.Net
|
|||||||
|
|
||||||
if (s7Data.Length < 15) throw NotEnoughBytes();
|
if (s7Data.Length < 15) throw NotEnoughBytes();
|
||||||
|
|
||||||
if (s7Data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7Data[14]);
|
||||||
throw new PlcException(ErrorCode.ReadData,
|
|
||||||
$"Invalid response from PLC: '{BitConverter.ToString(s7Data)}'.");
|
|
||||||
|
|
||||||
if (s7Data.Length < expectedLength) throw NotEnoughBytes();
|
if (s7Data.Length < expectedLength) throw NotEnoughBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ValidateResponseCode(ReadWriteErrorCode statusCode)
|
||||||
|
{
|
||||||
|
switch (statusCode)
|
||||||
|
{
|
||||||
|
case ReadWriteErrorCode.ObjectDoesNotExist:
|
||||||
|
throw new Exception("Received error from PLC: Object does not exist.");
|
||||||
|
case ReadWriteErrorCode.DataTypeInconsistent:
|
||||||
|
throw new Exception("Received error from PLC: Data type inconsistent.");
|
||||||
|
case ReadWriteErrorCode.DataTypeNotSupported:
|
||||||
|
throw new Exception("Received error from PLC: Data type not supported.");
|
||||||
|
case ReadWriteErrorCode.AccessingObjectNotAllowed:
|
||||||
|
throw new Exception("Received error from PLC: Accessing object not allowed.");
|
||||||
|
case ReadWriteErrorCode.AddressOutOfRange:
|
||||||
|
throw new Exception("Received error from PLC: Address out of range.");
|
||||||
|
case ReadWriteErrorCode.HardwareFault:
|
||||||
|
throw new Exception("Received error from PLC: Hardware fault.");
|
||||||
|
case ReadWriteErrorCode.Success:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception( $"Invalid response from PLC: statusCode={(byte)statusCode}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region IDisposable Support
|
#region IDisposable Support
|
||||||
private bool disposedValue = false; // To detect redundant calls
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ namespace S7.Net
|
|||||||
|
|
||||||
case VarType.String:
|
case VarType.String:
|
||||||
return Types.String.FromByteArray(bytes);
|
return Types.String.FromByteArray(bytes);
|
||||||
case VarType.StringEx:
|
case VarType.S7String:
|
||||||
return StringEx.FromByteArray(bytes);
|
return S7String.FromByteArray(bytes);
|
||||||
|
|
||||||
case VarType.Timer:
|
case VarType.Timer:
|
||||||
if (varCount == 1)
|
if (varCount == 1)
|
||||||
@@ -186,7 +186,7 @@ namespace S7.Net
|
|||||||
return (varCount < 1) ? 1 : varCount;
|
return (varCount < 1) ? 1 : varCount;
|
||||||
case VarType.String:
|
case VarType.String:
|
||||||
return varCount;
|
return varCount;
|
||||||
case VarType.StringEx:
|
case VarType.S7String:
|
||||||
return varCount + 2;
|
return varCount + 2;
|
||||||
case VarType.Word:
|
case VarType.Word:
|
||||||
case VarType.Timer:
|
case VarType.Timer:
|
||||||
|
|||||||
@@ -259,8 +259,7 @@ namespace S7.Net
|
|||||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||||
|
|
||||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken); //TODO use Async
|
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken); //TODO use Async
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
|
||||||
|
|
||||||
ParseDataIntoDataItems(s7data, dataItems);
|
ParseDataIntoDataItems(s7data, dataItems);
|
||||||
}
|
}
|
||||||
@@ -483,10 +482,7 @@ namespace S7.Net
|
|||||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken);
|
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken);
|
||||||
|
|
||||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -509,10 +505,7 @@ namespace S7.Net
|
|||||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||||
|
|
||||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -398,10 +398,7 @@ namespace S7.Net
|
|||||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||||
|
|
||||||
var s7data = COTP.TSDU.Read(stream);
|
var s7data = COTP.TSDU.Read(stream);
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
@@ -483,10 +480,7 @@ namespace S7.Net
|
|||||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||||
|
|
||||||
var s7data = COTP.TSDU.Read(stream);
|
var s7data = COTP.TSDU.Read(stream);
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception exc)
|
catch (Exception exc)
|
||||||
{
|
{
|
||||||
@@ -524,8 +518,8 @@ namespace S7.Net
|
|||||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||||
|
|
||||||
var s7data = COTP.TSDU.Read(stream); //TODO use Async
|
var s7data = COTP.TSDU.Read(stream); //TODO use Async
|
||||||
if (s7data == null || s7data[14] != 0xff)
|
|
||||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||||
|
|
||||||
ParseDataIntoDataItems(s7data, dataItems);
|
ParseDataIntoDataItems(s7data, dataItems);
|
||||||
}
|
}
|
||||||
|
|||||||
15
S7.Net/Protocol/ReadWriteErrorCode.cs
Normal file
15
S7.Net/Protocol/ReadWriteErrorCode.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
namespace S7.Net.Protocol
|
||||||
|
{
|
||||||
|
internal enum ReadWriteErrorCode : byte
|
||||||
|
{
|
||||||
|
Reserved = 0x00,
|
||||||
|
HardwareFault = 0x01,
|
||||||
|
AccessingObjectNotAllowed = 0x03,
|
||||||
|
AddressOutOfRange = 0x05,
|
||||||
|
DataTypeNotSupported = 0x06,
|
||||||
|
DataTypeInconsistent = 0x07,
|
||||||
|
ObjectDoesNotExist = 0x0a,
|
||||||
|
Success = 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,11 +94,16 @@ namespace S7.Net.Protocol
|
|||||||
|
|
||||||
for (int i = 0; i < dataItems.Length; i++)
|
for (int i = 0; i < dataItems.Length; i++)
|
||||||
{
|
{
|
||||||
var result = itemResults[i];
|
try
|
||||||
if (result == 0xff) continue;
|
{
|
||||||
|
Plc.ValidateResponseCode((ReadWriteErrorCode)itemResults[i]);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
if (errors == null) errors = new List<Exception>();
|
if (errors == null) errors = new List<Exception>();
|
||||||
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed with error code {result}."));
|
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}."));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors != null)
|
if (errors != null)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ namespace S7.Net.Protocol
|
|||||||
throw new Exception($"DataItem.Value is null, cannot serialize. StartAddr={dataItem.StartByteAdr} VarType={dataItem.VarType}");
|
throw new Exception($"DataItem.Value is null, cannot serialize. StartAddr={dataItem.StartByteAdr} VarType={dataItem.VarType}");
|
||||||
}
|
}
|
||||||
if (dataItem.Value is string s)
|
if (dataItem.Value is string s)
|
||||||
return dataItem.VarType == VarType.StringEx
|
return dataItem.VarType == VarType.S7String
|
||||||
? StringEx.ToByteArray(s, dataItem.Count)
|
? S7String.ToByteArray(s, dataItem.Count)
|
||||||
: Types.String.ToByteArray(s, dataItem.Count);
|
: Types.String.ToByteArray(s, dataItem.Count);
|
||||||
|
|
||||||
return SerializeValue(dataItem.Value);
|
return SerializeValue(dataItem.Value);
|
||||||
|
|||||||
69
S7.Net/Types/S7String.cs
Normal file
69
S7.Net/Types/S7String.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace S7.Net.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the methods to convert from S7 strings to C# strings
|
||||||
|
/// An S7 String has a preceeding 2 byte header containing its capacity and length
|
||||||
|
/// </summary>
|
||||||
|
public static class S7String
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts S7 bytes to a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string FromByteArray(byte[] bytes)
|
||||||
|
{
|
||||||
|
if (bytes.Length < 2)
|
||||||
|
{
|
||||||
|
throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = bytes[0];
|
||||||
|
int length = bytes[1];
|
||||||
|
if (length > size)
|
||||||
|
{
|
||||||
|
throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / length larger than capacity");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Encoding.ASCII.GetString(bytes, 2, length);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new PlcException(ErrorCode.ReadData,
|
||||||
|
$"Failed to parse {VarType.S7String} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="T:string"/> to S7 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 (value is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}.");
|
||||||
|
|
||||||
|
var bytes = Encoding.ASCII.GetBytes(value);
|
||||||
|
if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength}).");
|
||||||
|
|
||||||
|
var buffer = new byte[2 + reservedLength];
|
||||||
|
Array.Copy(bytes, 0, buffer, 2, bytes.Length);
|
||||||
|
buffer[0] = (byte)reservedLength;
|
||||||
|
buffer[1] = (byte)bytes.Length;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace S7.Net.Types
|
namespace S7.Net.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the methods to convert from S7 strings to C# strings
|
/// Contains the methods to convert from S7 Array of Chars (like a const char[N] C-String) to C# strings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class String
|
public class String
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,70 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace S7.Net.Types
|
namespace S7.Net.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc cref="S7String"/>
|
||||||
/// Contains the methods to convert from S7 strings to C# strings
|
[Obsolete("Please use S7String class")]
|
||||||
/// there are two kinds how strings a send. This one is with a pre of two bytes
|
|
||||||
/// they contain the length of the string
|
|
||||||
/// </summary>
|
|
||||||
public static class StringEx
|
public static class StringEx
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc cref="S7String.FromByteArray(byte[])"/>
|
||||||
/// Converts S7 bytes to a string
|
public static string FromByteArray(byte[] bytes) => S7String.FromByteArray(bytes);
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string FromByteArray(byte[] bytes)
|
|
||||||
{
|
|
||||||
if (bytes.Length < 2)
|
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / too short");
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = bytes[0];
|
/// <inheritdoc cref="S7String.ToByteArray(string, int)"/>
|
||||||
int length = bytes[1];
|
public static byte[] ToByteArray(string value, int reservedLength) => S7String.ToByteArray(value, reservedLength);
|
||||||
if (length > size)
|
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.ReadData, "Malformed S7 String / length larger than capacity");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Encoding.ASCII.GetString(bytes, 2, length);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new PlcException(ErrorCode.ReadData,
|
|
||||||
$"Failed to parse {VarType.StringEx} from data. Following fields were read: size: '{size}', actual length: '{length}', total number of bytes (including header): '{bytes.Length}'.",
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a <see cref="T:string"/> to S7 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 (value is null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reservedLength > byte.MaxValue) throw new ArgumentException($"The maximum string length supported is {byte.MaxValue}.");
|
|
||||||
|
|
||||||
var bytes = Encoding.ASCII.GetBytes(value);
|
|
||||||
if (bytes.Length > reservedLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({reservedLength}).");
|
|
||||||
|
|
||||||
var buffer = new byte[2 + reservedLength];
|
|
||||||
Array.Copy(bytes, 0, buffer, 2, bytes.Length);
|
|
||||||
buffer[0] = (byte)reservedLength;
|
|
||||||
buffer[1] = (byte)bytes.Length;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user