I have one plc instance in whole application, and open it at start of application. Then, I start to monitor up to 10 plc DataBlocks which is all the same structure with different threads. But sometimes (may be running for a day or two), it will throw exception which indicate the bytes read error as below:
S7.Net.PlcException: Received 38 bytes: '32-03-00-00-00-00-00-02-00-18-00-00-04-01-FF-04-00-A0-04-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00', expected 372 bytes.
And then the byte[] to string conversion fails with exception threw by line 28 or 37 in S7String.cs.
the application can simplified as the code shows
// main
static async Task Main(string[] args)
{
using var plc = new Plc(CpuType.S71500, "127.0.0.1", 102, 0, 1);
await plc.OpenAsync();
var dbList = new int[] { 100, 200, 300, 400, 500 };
var tasks = dbList.Select(db => MonitorDataBlockAsync(plc, db));
await Task.WhenAll(tasks);
}
// monitor dataBlock
private static async Task MonitorDataBlockAsync(Plc plc, int db, CancellationToken token = default)
{
while (true)
{
token.ThrowIfCancellationRequested();
try
{
var result = await plc.RetrieveFromPlcAsync<HmiInfo>(db);
System.Console.WriteLine(result.ToString());
}
catch (Exception exc)
{
System.Console.WriteLine(exc);
}
finally
{
await Task.Delay(1000, token);
}
}
}
// the HmiInfo class
public class HmiInfo : PtoBase
{
public byte[] ByteArr1 { get; set; } = new byte[500];
public string ByteArr1String => S7String.FromByteArray(ByteArr1);
public byte[] ByteArr2 { get; set; } = new byte[500];
public string ByteArr2String => S7String.FromByteArray(ByteArr2);
public override int Offset => 0;
public override string ToString() => $"ByteArr1: {ByteArr1String}, ByteArr2: {ByteArr2String}";
}
public abstract class PtoBase
{
public abstract int Offset { get; }
public virtual string ToJson()
{
var result = JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings()
{
ContractResolver = new ByteContractResolver(),
});
return result;
}
}
// plc extensions
public static class S7NetPlusExtensions
{
public static async Task<T> RetrieveFromPlcAsync<T>(this Plc plc, int db) where T : PtoBase, new()
=> await plc.RetrieveFromPlcWithOffsetAsync<T>(db, new T().Offset);
public static async Task<T> RetrieveFromPlcWithOffsetAsync<T>(this Plc plc, int db, int offset, int timeoutMs = 0) where T : PtoBase, new()
{
if (timeoutMs == 0) timeoutMs = 5000;
async Task<T> ReadAsync(Plc p) => await p.ReadClassAsync<T>(db, offset, new CancellationTokenSource(timeoutMs).Token);
return await OperatePlcWithRetryAsync(plc, async p => await ReadAsync(plc));
}
private static async Task<T> OperatePlcWithRetryAsync<T>(Plc plc, Func<Plc, Task<T>> operation)
{
var i = 0;
while (i++ < 5)
{
try
{
return await operation(plc);
}
catch (Exception e)
{
System.Console.WriteLine(e);
}
}
System.Console.WriteLine("Error after 5 retries");
throw new Exception();
}
}
I have one plc instance in whole application, and open it at start of application. Then, I start to monitor up to 10 plc DataBlocks which is all the same structure with different threads. But sometimes (may be running for a day or two), it will throw exception which indicate the bytes read error as below:
S7.Net.PlcException: Received 38 bytes: '32-03-00-00-00-00-00-02-00-18-00-00-04-01-FF-04-00-A0-04-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00', expected 372 bytes.
And then the
byte[]
tostring
conversion fails with exception threw by line 28 or 37 inS7String.cs
.the application can simplified as the code shows