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
|
||||
|
||||
Unit tests use Snap7 server, so port 102 must be not in use.
|
||||
If you have Siemens Step7 installed, the service s7oiehsx64 is stopped when running unit tests.
|
||||
You have to restart the service manually if you need it.
|
||||
Unit tests use Snap7 server.
|
||||
On Windows, the DLL is included with the test project. On other platforms, Snap7 must be installed manually before running tests.
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace S7.Net.UnitTest.Helpers
|
||||
Console.WriteLine(Server.EventText(ref Event));
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
public static void Start(short port)
|
||||
{
|
||||
Server = new S7Server();
|
||||
// Share some resources with our virtual PLC
|
||||
@@ -59,7 +59,14 @@ namespace S7.Net.UnitTest.Helpers
|
||||
// Start the server onto the default adapter.
|
||||
// To select an adapter we have to use Server->StartTo("192.168.x.y").
|
||||
// Start() is the same of StartTo("0.0.0.0");
|
||||
|
||||
Server.SetParam(S7Consts.p_u16_LocalPort, ref port);
|
||||
|
||||
int Error = Server.Start();
|
||||
if (Error != 0)
|
||||
{
|
||||
throw new Exception($"Error starting Snap7 server: {Server.ErrorText(Error)}");
|
||||
}
|
||||
//if (Error == 0)
|
||||
//{
|
||||
// // Now the server is running ... wait a key to terminate
|
||||
|
||||
@@ -7,13 +7,15 @@ using S7.Net;
|
||||
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using S7.Net.Protocol;
|
||||
using System.Collections;
|
||||
|
||||
namespace S7.Net.UnitTest
|
||||
{
|
||||
[TestClass]
|
||||
public class ProtocolUnitTest
|
||||
{
|
||||
private TestContext TestContext { get; set; }
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestMethod]
|
||||
public async Task TPKT_Read()
|
||||
@@ -64,6 +66,32 @@ namespace S7.Net.UnitTest
|
||||
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
|
||||
.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">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net452</TargetFrameworks>
|
||||
<TargetFrameworks>net452;netcoreapp3.1</TargetFrameworks>
|
||||
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>Properties\S7.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<IsPackable>false</IsPackable>
|
||||
<Copyright>Copyright © 2014</Copyright>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||
@@ -24,13 +26,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="snap7.dll">
|
||||
<None Update="runtimes\win-x64\native\snap7.dll" Link="snap7.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
#region Using
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net;
|
||||
using S7.Net.UnitTest.Helpers;
|
||||
using S7.Net.UnitTest;
|
||||
using System.ServiceProcess;
|
||||
using S7.Net.Types;
|
||||
using S7.UnitTest.Helpers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
#region Using
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using S7.Net;
|
||||
using S7.Net.UnitTest.Helpers;
|
||||
using S7.Net.UnitTest;
|
||||
using System.ServiceProcess;
|
||||
using S7.Net.Types;
|
||||
using S7.UnitTest.Helpers;
|
||||
|
||||
@@ -41,6 +37,8 @@ namespace S7.Net.UnitTest
|
||||
#region Constants
|
||||
const int DB2 = 2;
|
||||
const int DB4 = 4;
|
||||
const short TestServerPort = 31122;
|
||||
const string TestServerIp = "127.0.0.1";
|
||||
#endregion
|
||||
|
||||
#region Private fields
|
||||
@@ -53,16 +51,19 @@ namespace S7.Net.UnitTest
|
||||
/// </summary>
|
||||
public S7NetTests()
|
||||
{
|
||||
plc = new Plc(CpuType.S7300, "127.0.0.1", 0, 2);
|
||||
//ConsoleManager.Show();
|
||||
ShutDownServiceS7oiehsx64();
|
||||
plc = CreatePlc();
|
||||
|
||||
}
|
||||
|
||||
private static Plc CreatePlc()
|
||||
{
|
||||
return new Plc(CpuType.S7300, TestServerIp, TestServerPort, 0, 2);
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
S7TestServer.Start();
|
||||
S7TestServer.Start(TestServerPort);
|
||||
plc.Open();
|
||||
}
|
||||
|
||||
@@ -931,9 +932,9 @@ namespace S7.Net.UnitTest
|
||||
{
|
||||
plc.Close();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1026,18 +1027,6 @@ namespace S7.Net.UnitTest
|
||||
#endregion
|
||||
|
||||
#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
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
@@ -36,11 +36,8 @@ namespace Snap7
|
||||
|
||||
public class S7Consts
|
||||
{
|
||||
#if __MonoCS__ // Assuming that we are using Unix release of Mono (otherwise modify it)
|
||||
public const string Snap7LibName = "libsnap7.so";
|
||||
#else
|
||||
public const string Snap7LibName = "snap7.dll";
|
||||
#endif
|
||||
public const string Snap7LibName = "snap7";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PARAMS LIST
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace S7.Net.UnitTest
|
||||
[TestClass]
|
||||
public class StreamTests
|
||||
{
|
||||
private TestContext TestContext { get; set; }
|
||||
public TestContext TestContext { get; set; }
|
||||
|
||||
[TestMethod]
|
||||
public async Task TPKT_ReadRestrictedStreamAsync()
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
namespace S7.Net.UnitTest.TypeTests
|
||||
{
|
||||
[TestClass]
|
||||
public class StringExTests
|
||||
public class S7StringTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void ReadEmptyStringWithZeroByteLength()
|
||||
@@ -36,7 +36,7 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
[TestMethod]
|
||||
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]
|
||||
@@ -102,7 +102,7 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
[TestMethod]
|
||||
public void WriteAbcWithStringLargetThanReservedLength()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => StringEx.ToByteArray("Abc", 2));
|
||||
Assert.ThrowsException<ArgumentException>(() => S7String.ToByteArray("Abc", 2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -119,16 +119,16 @@ namespace S7.Net.UnitTest.TypeTests
|
||||
|
||||
private static void AssertFromByteArrayEquals(string expected, params byte[] bytes)
|
||||
{
|
||||
var convertedString = StringEx.FromByteArray(bytes);
|
||||
var convertedString = S7String.FromByteArray(bytes);
|
||||
Assert.AreEqual(expected, convertedString);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
var convertedBack = StringEx.FromByteArray(convertedData);
|
||||
var convertedBack = S7String.FromByteArray(convertedData);
|
||||
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,
|
||||
|
||||
/// <summary>
|
||||
/// String variable type (variable)
|
||||
/// Char Array / C-String variable type (variable)
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// String variable type (variable)
|
||||
/// S7 String variable type (variable)
|
||||
/// </summary>
|
||||
StringEx,
|
||||
S7String,
|
||||
|
||||
/// <summary>
|
||||
/// Timer variable type
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using S7.Net.Protocol;
|
||||
using S7.Net.Types;
|
||||
|
||||
|
||||
@@ -18,8 +19,8 @@ namespace S7.Net
|
||||
private TcpClient? tcpClient;
|
||||
private NetworkStream? _stream;
|
||||
|
||||
private int readTimeout = System.Threading.Timeout.Infinite;
|
||||
private int writeTimeout = System.Threading.Timeout.Infinite;
|
||||
private int readTimeout = 0; // default no timeout
|
||||
private int writeTimeout = 0; // default no timeout
|
||||
|
||||
/// <summary>
|
||||
/// IP address of the PLC
|
||||
@@ -234,13 +235,34 @@ namespace S7.Net
|
||||
|
||||
if (s7Data.Length < 15) throw NotEnoughBytes();
|
||||
|
||||
if (s7Data[14] != 0xff)
|
||||
throw new PlcException(ErrorCode.ReadData,
|
||||
$"Invalid response from PLC: '{BitConverter.ToString(s7Data)}'.");
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7Data[14]);
|
||||
|
||||
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
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ namespace S7.Net
|
||||
|
||||
case VarType.String:
|
||||
return Types.String.FromByteArray(bytes);
|
||||
case VarType.StringEx:
|
||||
return StringEx.FromByteArray(bytes);
|
||||
case VarType.S7String:
|
||||
return S7String.FromByteArray(bytes);
|
||||
|
||||
case VarType.Timer:
|
||||
if (varCount == 1)
|
||||
@@ -186,7 +186,7 @@ namespace S7.Net
|
||||
return (varCount < 1) ? 1 : varCount;
|
||||
case VarType.String:
|
||||
return varCount;
|
||||
case VarType.StringEx:
|
||||
case VarType.S7String:
|
||||
return varCount + 2;
|
||||
case VarType.Word:
|
||||
case VarType.Timer:
|
||||
|
||||
@@ -259,8 +259,7 @@ namespace S7.Net
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken); //TODO use Async
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
|
||||
ParseDataIntoDataItems(s7data, dataItems);
|
||||
}
|
||||
@@ -483,10 +482,7 @@ namespace S7.Net
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length, cancellationToken);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -509,10 +505,7 @@ namespace S7.Net
|
||||
await stream.WriteAsync(dataToSend, 0, dataToSend.Length);
|
||||
|
||||
var s7data = await COTP.TSDU.ReadAsync(stream, cancellationToken);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -398,10 +398,7 @@ namespace S7.Net
|
||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
@@ -483,10 +480,7 @@ namespace S7.Net
|
||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||
|
||||
var s7data = COTP.TSDU.Read(stream);
|
||||
if (s7data == null || s7data[14] != 0xff)
|
||||
{
|
||||
throw new PlcException(ErrorCode.WrongNumberReceivedBytes);
|
||||
}
|
||||
ValidateResponseCode((ReadWriteErrorCode)s7data[14]);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
@@ -524,8 +518,8 @@ namespace S7.Net
|
||||
stream.Write(dataToSend, 0, dataToSend.Length);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
var result = itemResults[i];
|
||||
if (result == 0xff) continue;
|
||||
try
|
||||
{
|
||||
Plc.ValidateResponseCode((ReadWriteErrorCode)itemResults[i]);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if (errors == null) errors = new List<Exception>();
|
||||
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}."));
|
||||
}
|
||||
|
||||
if (errors == null) errors = new List<Exception>();
|
||||
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed with error code {result}."));
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
if (dataItem.Value is string s)
|
||||
return dataItem.VarType == VarType.StringEx
|
||||
? StringEx.ToByteArray(s, dataItem.Count)
|
||||
return dataItem.VarType == VarType.S7String
|
||||
? S7String.ToByteArray(s, dataItem.Count)
|
||||
: Types.String.ToByteArray(s, dataItem.Count);
|
||||
|
||||
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
|
||||
{
|
||||
/// <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>
|
||||
public class String
|
||||
{
|
||||
|
||||
@@ -1,70 +1,15 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace S7.Net.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the methods to convert from S7 strings to C# strings
|
||||
/// 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>
|
||||
/// <inheritdoc cref="S7String"/>
|
||||
[Obsolete("Please use S7String class")]
|
||||
public static class StringEx
|
||||
{
|
||||
/// <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");
|
||||
}
|
||||
/// <inheritdoc cref="S7String.FromByteArray(byte[])"/>
|
||||
public static string FromByteArray(byte[] bytes) => S7String.FromByteArray(bytes);
|
||||
|
||||
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.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;
|
||||
}
|
||||
/// <inheritdoc cref="S7String.ToByteArray(string, int)"/>
|
||||
public static byte[] ToByteArray(string value, int reservedLength) => S7String.ToByteArray(value, reservedLength);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user