This commit is contained in:
Peter Butzhammer
2024-02-07 09:35:21 +01:00
parent c79e07be33
commit babbb1a6bc
20 changed files with 372 additions and 374 deletions

View File

@@ -18,62 +18,39 @@ namespace Sharp7.Rx
{
internal class Sharp7Connector : IS7Connector
{
private readonly IS7VariableNameParser variableNameParser;
private readonly BehaviorSubject<ConnectionState> connectionStateSubject = new BehaviorSubject<ConnectionState>(Enums.ConnectionState.Initial);
private readonly int cpuSlotNr;
private readonly CompositeDisposable disposables = new CompositeDisposable();
private readonly LimitedConcurrencyLevelTaskScheduler scheduler = new LimitedConcurrencyLevelTaskScheduler(maxDegreeOfParallelism:1);
private readonly string ipAddress;
private readonly int port;
private readonly int rackNr;
private readonly int cpuSlotNr;
private readonly int port;
private S7Client sharp7;
private readonly LimitedConcurrencyLevelTaskScheduler scheduler = new LimitedConcurrencyLevelTaskScheduler(maxDegreeOfParallelism: 1);
private readonly IS7VariableNameParser variableNameParser;
private bool disposed;
public ILogger Logger { get; set; }
public async Task<Dictionary<string, byte[]>> ExecuteMultiVarRequest(IReadOnlyList<string> variableNames)
{
if (variableNames.IsEmpty())
return new Dictionary<string, byte[]>();
private S7Client sharp7;
var s7MultiVar = new S7MultiVar(sharp7);
var buffers = variableNames
.Select(key => new {VariableName = key, Address = variableNameParser.Parse(key)})
.Select(x =>
{
var buffer = new byte[x.Address.Length];
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, x.Address.DbNr, x.Address.Start,x.Address.Length, ref buffer);
return new { x.VariableName, Buffer = buffer};
})
.ToArray();
var result = await Task.Factory.StartNew(() => s7MultiVar.Read(), CancellationToken.None, TaskCreationOptions.None, scheduler);
if (result != 0)
{
EvaluateErrorCode(result);
throw new InvalidOperationException($"Error in MultiVar request for variables: {string.Join(",", variableNames)}");
}
return buffers.ToDictionary(arg => arg.VariableName, arg => arg.Buffer);
}
public Sharp7Connector(PlcConnectionSettings settings, IS7VariableNameParser variableNameParser)
{
{
this.variableNameParser = variableNameParser;
ipAddress = settings.IpAddress;
cpuSlotNr = settings.CpuMpiAddress;
port = settings.Port;
rackNr = settings.RackNumber;
port = settings.Port;
rackNr = settings.RackNumber;
ReconnectDelay = TimeSpan.FromSeconds(5);
ReconnectDelay = TimeSpan.FromSeconds(5);
}
public IObservable<ConnectionState> ConnectionState => connectionStateSubject.DistinctUntilChanged().AsObservable();
public ILogger Logger { get; set; }
public TimeSpan ReconnectDelay { get; set; }
private bool IsConnected => connectionStateSubject.Value == Enums.ConnectionState.Connected;
public void Dispose()
{
Dispose(true);
@@ -103,8 +80,6 @@ namespace Sharp7.Rx
return false;
}
public IObservable<ConnectionState> ConnectionState => connectionStateSubject.DistinctUntilChanged().AsObservable();
public async Task Disconnect()
{
@@ -112,14 +87,41 @@ namespace Sharp7.Rx
await CloseConnection();
}
public async Task<Dictionary<string, byte[]>> ExecuteMultiVarRequest(IReadOnlyList<string> variableNames)
{
if (variableNames.IsEmpty())
return new Dictionary<string, byte[]>();
var s7MultiVar = new S7MultiVar(sharp7);
var buffers = variableNames
.Select(key => new {VariableName = key, Address = variableNameParser.Parse(key)})
.Select(x =>
{
var buffer = new byte[x.Address.Length];
s7MultiVar.Add(S7Consts.S7AreaDB, S7Consts.S7WLByte, x.Address.DbNr, x.Address.Start, x.Address.Length, ref buffer);
return new {x.VariableName, Buffer = buffer};
})
.ToArray();
var result = await Task.Factory.StartNew(() => s7MultiVar.Read(), CancellationToken.None, TaskCreationOptions.None, scheduler);
if (result != 0)
{
EvaluateErrorCode(result);
throw new InvalidOperationException($"Error in MultiVar request for variables: {string.Join(",", variableNames)}");
}
return buffers.ToDictionary(arg => arg.VariableName, arg => arg.Buffer);
}
public Task InitializeAsync()
{
try
{
sharp7 = new S7Client();
sharp7.PLCPort = port;
sharp7.PLCPort = port;
var subscription =
var subscription =
ConnectionState
.Where(state => state == Enums.ConnectionState.ConnectionLost)
.Take(1)
@@ -132,12 +134,70 @@ namespace Sharp7.Rx
}
catch (Exception ex)
{
Logger?.LogError(ex, StringResources.StrErrorS7DriverCouldNotBeInitialized);
}
Logger?.LogError(ex, StringResources.StrErrorS7DriverCouldNotBeInitialized);
}
return Task.FromResult(true);
}
public async Task<byte[]> ReadBytes(Operand operand, ushort startByteAddress, ushort bytesToRead, ushort dBNr, CancellationToken token)
{
EnsureConnectionValid();
var buffer = new byte[bytesToRead];
var area = FromOperand(operand);
var result =
await Task.Factory.StartNew(() => sharp7.ReadArea(area, dBNr, startByteAddress, bytesToRead, S7Consts.S7WLByte, buffer), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
var errorText = sharp7.ErrorText(result);
throw new InvalidOperationException($"Error reading {operand}{dBNr}:{startByteAddress}->{bytesToRead} ({errorText})");
}
return buffer;
}
public async Task<bool> WriteBit(Operand operand, ushort startByteAddress, byte bitAdress, bool value, ushort dbNr, CancellationToken token)
{
EnsureConnectionValid();
var buffer = new[] {value ? (byte) 0xff : (byte) 0};
var offsetStart = (startByteAddress * 8) + bitAdress;
var result = await Task.Factory.StartNew(() => sharp7.WriteArea(FromOperand(operand), dbNr, offsetStart, 1, S7Consts.S7WLBit, buffer), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
return (false);
}
return (true);
}
public async Task<ushort> WriteBytes(Operand operand, ushort startByteAdress, byte[] data, ushort dBNr, CancellationToken token)
{
EnsureConnectionValid();
var result = await Task.Factory.StartNew(() => sharp7.WriteArea(FromOperand(operand), dBNr, startByteAdress, data.Length, S7Consts.S7WLByte, data), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
return 0;
}
return (ushort) (data.Length);
}
protected virtual void Dispose(bool disposing)
{
@@ -169,6 +229,18 @@ namespace Sharp7.Rx
await Task.Factory.StartNew(() => sharp7.Disconnect(), CancellationToken.None, TaskCreationOptions.None, scheduler);
}
private void EnsureConnectionValid()
{
if (disposed)
throw new ObjectDisposedException("S7Connector");
if (sharp7 == null)
throw new InvalidOperationException(StringResources.StrErrorS7DriverNotInitialized);
if (!IsConnected)
throw new InvalidOperationException("Plc is not connected");
}
private bool EvaluateErrorCode(int errorCode)
{
if (errorCode == 0)
@@ -186,6 +258,23 @@ namespace Sharp7.Rx
return false;
}
private int FromOperand(Operand operand)
{
switch (operand)
{
case Operand.Input:
return S7Consts.S7AreaPE;
case Operand.Output:
return S7Consts.S7AreaPA;
case Operand.Marker:
return S7Consts.S7AreaMK;
case Operand.Db:
return S7Consts.S7AreaDB;
default:
throw new ArgumentOutOfRangeException(nameof(operand), operand, null);
}
}
private async Task<bool> Reconnect()
{
await CloseConnection();
@@ -204,91 +293,5 @@ namespace Sharp7.Rx
{
Dispose(false);
}
private bool IsConnected => connectionStateSubject.Value == Enums.ConnectionState.Connected;
public async Task<byte[]> ReadBytes(Operand operand, ushort startByteAddress, ushort bytesToRead, ushort dBNr, CancellationToken token)
{
EnsureConnectionValid();
var buffer = new byte[bytesToRead];
var area = FromOperand(operand);
var result =
await Task.Factory.StartNew(() => sharp7.ReadArea(area, dBNr, startByteAddress, bytesToRead, S7Consts.S7WLByte, buffer), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
var errorText = sharp7.ErrorText(result);
throw new InvalidOperationException($"Error reading {operand}{dBNr}:{startByteAddress}->{bytesToRead} ({errorText})");
}
return buffer;
}
private int FromOperand(Operand operand)
{
switch (operand)
{
case Operand.Input:
return S7Consts.S7AreaPE;
case Operand.Output:
return S7Consts.S7AreaPA;
case Operand.Marker:
return S7Consts.S7AreaMK;
case Operand.Db:
return S7Consts.S7AreaDB;
default:
throw new ArgumentOutOfRangeException(nameof(operand), operand, null);
}
}
private void EnsureConnectionValid()
{
if (disposed)
throw new ObjectDisposedException("S7Connector");
if (sharp7 == null)
throw new InvalidOperationException(StringResources.StrErrorS7DriverNotInitialized);
if (!IsConnected)
throw new InvalidOperationException("Plc is not connected");
}
public async Task<ushort> WriteBytes(Operand operand, ushort startByteAdress, byte[] data, ushort dBNr, CancellationToken token)
{
EnsureConnectionValid();
var result = await Task.Factory.StartNew(() => sharp7.WriteArea(FromOperand(operand), dBNr, startByteAdress, data.Length, S7Consts.S7WLByte, data), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
return 0;
}
return (ushort)(data.Length);
}
public async Task<bool> WriteBit(Operand operand, ushort startByteAddress, byte bitAdress, bool value, ushort dbNr, CancellationToken token)
{
EnsureConnectionValid();
var buffer = new byte[] { value ? (byte)0xff : (byte)0 };
var offsetStart = (startByteAddress * 8) + bitAdress;
var result = await Task.Factory.StartNew(() => sharp7.WriteArea(FromOperand(operand), dbNr, offsetStart, 1, S7Consts.S7WLBit, buffer), token, TaskCreationOptions.None, scheduler);
token.ThrowIfCancellationRequested();
if (result != 0)
{
EvaluateErrorCode(result);
return (false);
}
return (true);
}
}
}