diff --git a/S7.Net.UnitTest/ProtocolTests.cs b/S7.Net.UnitTest/ProtocolTests.cs index b18ebe8..2051edb 100644 --- a/S7.Net.UnitTest/ProtocolTests.cs +++ b/S7.Net.UnitTest/ProtocolTests.cs @@ -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(() => Plc.ValidateResponseCode(entry.StatusCode)); + } + else + { + Plc.ValidateResponseCode(entry.StatusCode); + } + } + } } } diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs index 96f2474..972c615 100644 --- a/S7.Net/PLC.cs +++ b/S7.Net/PLC.cs @@ -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 diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index 1de0ac8..fba1f9f 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -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) { diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 8cf5f89..ddd165c 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -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); } diff --git a/S7.Net/Protocol/ReadWriteErrorCode.cs b/S7.Net/Protocol/ReadWriteErrorCode.cs new file mode 100644 index 0000000..9d6b943 --- /dev/null +++ b/S7.Net/Protocol/ReadWriteErrorCode.cs @@ -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 + } +} diff --git a/S7.Net/Protocol/S7WriteMultiple.cs b/S7.Net/Protocol/S7WriteMultiple.cs index 853e1e5..9f0257f 100644 --- a/S7.Net/Protocol/S7WriteMultiple.cs +++ b/S7.Net/Protocol/S7WriteMultiple.cs @@ -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(); + errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed: {e.Message}.")); + } - if (errors == null) errors = new List(); - errors.Add(new Exception($"Write of dataItem {dataItems[i]} failed with error code {result}.")); } if (errors != null)