Add more extensive response code validation.

Fixes #310
This commit is contained in:
Serge Camille
2020-09-12 20:10:48 +02:00
parent 36a9ecb2c8
commit 1069641606
6 changed files with 84 additions and 27 deletions

View File

@@ -7,6 +7,8 @@ using S7.Net;
using System.IO;
using System.Threading.Tasks;
using S7.Net.Protocol;
using System.Collections;
namespace S7.Net.UnitTest
{
@@ -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);
}
}
}
}
}

View File

@@ -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;
@@ -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

View File

@@ -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)
{

View File

@@ -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);
}

View 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
}
}

View File

@@ -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 with error code {result}."));
errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}."));
}
}
if (errors != null)