using System; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Timer = System.Threading.Timer; namespace B24SiemensEmulator { public class SiemensTcpClient : IDisposable { private TcpClient? _tcpClient; private NetworkStream? _stream; private Timer? _sendTimer; private bool _isConnected = false; private bool _isRunning = false; private readonly object _lock = new object(); public event Action? ConnectionStatusChanged; public event Action? ResponseReceived; public event Action? ErrorOccurred; private readonly string _serverHost = "localhost"; private readonly int _serverPort = 42010; private readonly int _sendIntervalMs = 1000; public Func? GetPacketToSend; public bool IsConnected => _isConnected; public bool IsRunning => _isRunning; public async Task ConnectAsync() { try { if (_isConnected) return true; _tcpClient = new TcpClient(); await _tcpClient.ConnectAsync(_serverHost, _serverPort); _stream = _tcpClient.GetStream(); lock (_lock) { _isConnected = true; } ConnectionStatusChanged?.Invoke(true); return true; } catch (Exception ex) { ErrorOccurred?.Invoke($"Connection failed: {ex.Message}"); await DisconnectAsync(); return false; } } public async Task DisconnectAsync() { lock (_lock) { _isConnected = false; } _stream?.Close(); _stream?.Dispose(); _stream = null; _tcpClient?.Close(); _tcpClient?.Dispose(); _tcpClient = null; ConnectionStatusChanged?.Invoke(false); } public void StartSending() { if (_isRunning) return; _isRunning = true; _sendTimer = new Timer(SendPacketCallback, null, 0, _sendIntervalMs); } public void StopSending() { _isRunning = false; _sendTimer?.Dispose(); _sendTimer = null; } private async void SendPacketCallback(object? state) { if (!_isConnected || GetPacketToSend == null) return; try { var packet = GetPacketToSend(); await SendPacketAsync(packet); } catch (Exception ex) { ErrorOccurred?.Invoke($"Send failed: {ex.Message}"); await HandleConnectionLoss(); } } private async Task SendPacketAsync(PLCPacket packet) { if (_stream == null || !_isConnected) throw new InvalidOperationException("Not connected"); var data = packet.Serialize(); await _stream.WriteAsync(data, 0, data.Length); // Read response var responseBuffer = new byte[19]; // PLCPacket is 19 bytes int totalBytesRead = 0; while (totalBytesRead < responseBuffer.Length) { int bytesRead = await _stream.ReadAsync(responseBuffer, totalBytesRead, responseBuffer.Length - totalBytesRead); if (bytesRead == 0) throw new Exception("Server disconnected"); totalBytesRead += bytesRead; } var responsePacket = PLCPacket.Deserialize(responseBuffer); ResponseReceived?.Invoke(responsePacket); } private async Task HandleConnectionLoss() { await DisconnectAsync(); // Wait 1 second before trying to reconnect await Task.Delay(1000); if (_isRunning) { var reconnected = await ConnectAsync(); if (!reconnected) { // Schedule another reconnection attempt _ = Task.Run(async () => { await Task.Delay(1000); if (_isRunning && !_isConnected) { await HandleConnectionLoss(); } }); } } } public void Dispose() { StopSending(); DisconnectAsync().Wait(1000); } } }