diff --git a/S7.Net/PLC.cs b/S7.Net/PLC.cs index d62e783..d997b7a 100644 --- a/S7.Net/PLC.cs +++ b/S7.Net/PLC.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Net.Sockets; +using S7.Net.Types; namespace S7.Net @@ -178,6 +181,25 @@ namespace S7.Net } } + private void AssertPduSizeForRead(ICollection dataItems) + { + // 12 bytes of header data, 12 bytes of parameter data for each dataItem + if ((dataItems.Count + 1) * 12 > MaxPDUSize) throw new Exception("Too many vars requested for read"); + + // 14 bytes of header data, 4 bytes of result data for each dataItem and the actual data + if (GetDataLength(dataItems) + dataItems.Count * 4 + 14 > MaxPDUSize) throw new Exception("Too much data requested for read"); + } + + private void AssertPduSizeForWrite(ICollection dataItems) + { + // 12 bytes of header data, 18 bytes of parameter data for each dataItem + if (dataItems.Count * 18 + 12 > MaxPDUSize) throw new Exception("Too many vars supplied for write"); + + // 12 bytes of header data, 16 bytes of data for each dataItem and the actual data + if (GetDataLength(dataItems) + dataItems.Count * 16 + 12 > MaxPDUSize) + throw new Exception("Too much data supplied for write"); + } + private void ConfigureConnection() { if (tcpClient == null) @@ -189,6 +211,13 @@ namespace S7.Net tcpClient.SendTimeout = WriteTimeout; } + private int GetDataLength(IEnumerable dataItems) + { + // Odd length variables are 0-padded + return dataItems.Select(di => VarTypeToByteLength(di.VarType, di.Count)) + .Sum(len => (len & 1) == 1 ? len + 1 : len); + } + #region IDisposable Support private bool disposedValue = false; // To detect redundant calls diff --git a/S7.Net/PlcAsynchronous.cs b/S7.Net/PlcAsynchronous.cs index eb44f87..2a2de3e 100644 --- a/S7.Net/PlcAsynchronous.cs +++ b/S7.Net/PlcAsynchronous.cs @@ -205,15 +205,10 @@ namespace S7.Net /// List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted. public async Task> ReadMultipleVarsAsync(List dataItems) { - int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count)); - - //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. - if (dataItems.Count > 20) - throw new Exception("Too many vars requested"); - if (cntBytes > 222) - throw new Exception("Too many bytes requested"); // TODO: proper TDU check + split in multiple requests + AssertPduSizeForRead(dataItems); + try { // first create the header @@ -410,6 +405,8 @@ namespace S7.Net /// Task that completes when response from PLC is parsed. public async Task WriteAsync(params DataItem[] dataItems) { + AssertPduSizeForWrite(dataItems); + var message = new ByteArray(); var length = S7WriteMultiple.CreateRequest(message, dataItems); await stream.WriteAsync(message.Array, 0, length).ConfigureAwait(false); diff --git a/S7.Net/PlcSynchronous.cs b/S7.Net/PlcSynchronous.cs index 4d432d3..7d2ef01 100644 --- a/S7.Net/PlcSynchronous.cs +++ b/S7.Net/PlcSynchronous.cs @@ -374,6 +374,8 @@ namespace S7.Net /// The DataItem(s) to write to the PLC. public void Write(params DataItem[] dataItems) { + AssertPduSizeForWrite(dataItems); + var message = new ByteArray(); var length = S7WriteMultiple.CreateRequest(message, dataItems); stream.Write(message.Array, 0, length); @@ -479,15 +481,10 @@ namespace S7.Net /// List of dataitems that contains the list of variables that must be read. Maximum 20 dataitems are accepted. public void ReadMultipleVars(List dataItems) { - int cntBytes = dataItems.Sum(dataItem => VarTypeToByteLength(dataItem.VarType, dataItem.Count)); - - //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. - if (dataItems.Count > 20) - throw new Exception("Too many vars requested"); - if (cntBytes > 222) - throw new Exception("Too many bytes requested"); // TODO: proper TDU check + split in multiple requests + AssertPduSizeForRead(dataItems); + try { // first create the header