diff --git a/Sharp7.Rx/Sharp7Connector.cs b/Sharp7.Rx/Sharp7Connector.cs index 4597b5b..78c911f 100644 --- a/Sharp7.Rx/Sharp7Connector.cs +++ b/Sharp7.Rx/Sharp7Connector.cs @@ -116,12 +116,11 @@ internal class Sharp7Connector : IDisposable return buffers.ToDictionary(arg => arg.VariableName, arg => arg.Buffer); } - public Task InitializeAsync() + public void InitializeAsync() { try { - sharp7 = new S7Client(); - sharp7.PLCPort = port; + sharp7 = new S7Client {PLCPort = port}; var subscription = ConnectionState @@ -138,8 +137,6 @@ internal class Sharp7Connector : IDisposable { Logger?.LogError(ex, "S7 driver for {Connection} could not be initialized", ConnectionIdentifier); } - - return Task.FromResult(true); } public async Task ReadBytes(Operand operand, ushort startByteAddress, ushort bytesToRead, ushort dbNo, CancellationToken token) diff --git a/Sharp7.Rx/Sharp7Plc.cs b/Sharp7.Rx/Sharp7Plc.cs index 03bda1f..e77d51b 100644 --- a/Sharp7.Rx/Sharp7Plc.cs +++ b/Sharp7.Rx/Sharp7Plc.cs @@ -28,7 +28,7 @@ public class Sharp7Plc : IPlc private readonly ConcurrentSubjectDictionary multiVariableSubscriptions = new(StringComparer.InvariantCultureIgnoreCase); private readonly List performanceCounter = new(1000); private readonly PlcConnectionSettings plcConnectionSettings; - private readonly CacheVariableNameParser variableNameParser = new CacheVariableNameParser(new VariableNameParser()); + private readonly CacheVariableNameParser variableNameParser = new(new VariableNameParser()); private bool disposed; private int initialized; @@ -37,10 +37,10 @@ public class Sharp7Plc : IPlc /// /// - /// - /// - /// - /// + /// IP address of S7. + /// See Sharp7 wiki + /// See Sharp7 wiki + /// TCP port for communication /// /// /// Polling interval for multi variable read from PLC. @@ -55,7 +55,7 @@ public class Sharp7Plc : IPlc /// public Sharp7Plc(string ipAddress, int rackNumber, int cpuMpiAddress, int port = 102, TimeSpan? multiVarRequestCycleTime = null) { - plcConnectionSettings = new PlcConnectionSettings { IpAddress = ipAddress, RackNumber = rackNumber, CpuMpiAddress = cpuMpiAddress, Port = port }; + plcConnectionSettings = new PlcConnectionSettings {IpAddress = ipAddress, RackNumber = rackNumber, CpuMpiAddress = cpuMpiAddress, Port = port}; s7Connector = new Sharp7Connector(plcConnectionSettings, variableNameParser); ConnectionState = s7Connector.ConnectionState; @@ -90,10 +90,6 @@ public class Sharp7Plc : IPlc /// Create an Observable for a given variable. Multiple notifications are automatically combined into a multi-variable subscription to /// reduce network trafic and PLC workload. /// - /// - /// - /// - /// public IObservable CreateNotification(string variableName, TransmissionMode transmissionMode) { return Observable.Create(observer => @@ -128,12 +124,28 @@ public class Sharp7Plc : IPlc } /// - /// Read PLC variable as generic variable. + /// Creates an observable of object for a variable. + /// The return type is automatically infered from the variable name. + /// + /// The return type is infered from the variable name. + public IObservable CreateNotification(string variableName, TransmissionMode transmissionMode) + { + var address = variableNameParser.Parse(variableName); + var clrType = address.GetClrType(); + + var genericCreateNotification = createNotificationMethod!.MakeGenericMethod(clrType); + + var genericNotification = genericCreateNotification.Invoke(this, [variableName, transmissionMode]); + + return SignatureConverter.ConvertToObjectObservable(genericNotification, clrType); + } + + /// + /// Read PLC variable as generic variable. + /// + /// The method will fail with a , if is not . + /// /// - /// - /// - /// - /// public async Task GetValue(string variableName, CancellationToken token = default) { var address = ParseAndVerify(variableName, typeof(TValue)); @@ -145,9 +157,10 @@ public class Sharp7Plc : IPlc /// /// Read PLC variable as object. /// The return type is automatically infered from the variable name. + /// + /// The method will fail with a , if is not . + /// /// - /// - /// /// The actual return type is infered from the variable name. public async Task GetValue(string variableName, CancellationToken token = default) { @@ -168,12 +181,10 @@ public class Sharp7Plc : IPlc /// /// Write value to the PLC. + /// + /// The method will fail with a , if is not . + /// /// - /// - /// - /// - /// - /// public async Task SetValue(string variableName, TValue value, CancellationToken token = default) { var address = ParseAndVerify(variableName, typeof(TValue)); @@ -183,7 +194,7 @@ public class Sharp7Plc : IPlc // Special handling for bools, which are written on a by-bit basis. Writing a complete byte would // overwrite other bits within this byte. - await s7Connector.WriteBit(address.Operand, address.Start, address.Bit!.Value, (bool)(object)value, address.DbNo, token); + await s7Connector.WriteBit(address.Operand, address.Start, address.Bit!.Value, (bool) (object) value, address.DbNo, token); } else { @@ -201,25 +212,6 @@ public class Sharp7Plc : IPlc } } - /// - /// Creates an observable of object for a variable. - /// The return type is automatically infered from the variable name. - /// - /// - /// - /// The return type is infered from the variable name. - public IObservable CreateNotification(string variableName, TransmissionMode transmissionMode) - { - var address = variableNameParser.Parse(variableName); - var clrType = address.GetClrType(); - - var genericCreateNotification = createNotificationMethod!.MakeGenericMethod(clrType); - - var genericNotification = genericCreateNotification.Invoke(this, [variableName, transmissionMode]); - - return SignatureConverter.ConvertToObjectObservable(genericNotification, clrType); - } - /// /// Trigger PLC connection and start notification loop. /// @@ -228,26 +220,35 @@ public class Sharp7Plc : IPlc /// /// Always true [Obsolete($"Use {nameof(InitializeConnection)} or {nameof(TriggerConnection)}.")] - public async Task InitializeAsync() + public Task InitializeAsync() { - await TriggerConnection(); - return true; + TriggerConnection(); + return Task.FromResult(true); } /// - /// Initialize PLC connection and wait for connection to be established. + /// Initialize PLC connection and wait for connection to be established ( is ). /// /// /// - public async Task InitializeConnection(CancellationToken token = default) => await DoInitializeConnection(true, token); + public async Task InitializeConnection(CancellationToken token = default) + { + DoInitializeConnection(); + await s7Connector.ConnectionState + .FirstAsync(c => c == Enums.ConnectionState.Connected) + .ToTask(token); + } /// /// Initialize PLC and trigger connection. This method will not wait for the connection to be established. + /// + /// Without an established connection, it is safe to call , but + /// and will fail. + /// /// - /// /// - public async Task TriggerConnection(CancellationToken token = default) => await DoInitializeConnection(false, token); + public void TriggerConnection() => DoInitializeConnection(); protected virtual void Dispose(bool disposing) { @@ -270,11 +271,12 @@ public class Sharp7Plc : IPlc } } - private async Task DoInitializeConnection(bool waitForConnection, CancellationToken token) + private void DoInitializeConnection() { - if (Interlocked.Exchange(ref initialized, 1) == 1) return; + if (Interlocked.Exchange(ref initialized, 1) == 1) + return; - await s7Connector.InitializeAsync(); + s7Connector.InitializeAsync(); // Triger connection. // The initial connection might fail. In this case a reconnect is initiated. @@ -285,16 +287,11 @@ public class Sharp7Plc : IPlc { await s7Connector.Connect(); } - catch (Exception e) + catch (Exception) { // Ignore. Exception is logged in the connector } - }, token); - - if (waitForConnection) - await s7Connector.ConnectionState - .FirstAsync(c => c == Enums.ConnectionState.Connected) - .ToTask(token); + }); StartNotificationLoop(); } @@ -370,9 +367,4 @@ public class Sharp7Plc : IPlc // Subscription has already been created (race condition). Dispose new subscription. subscription.Dispose(); } - - ~Sharp7Plc() - { - Dispose(false); - } }