167 lines
4.7 KiB
C#
167 lines
4.7 KiB
C#
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<bool>? ConnectionStatusChanged;
|
|
public event Action<PLCPacket>? ResponseReceived;
|
|
public event Action<string>? ErrorOccurred;
|
|
|
|
private readonly string _serverHost = "localhost";
|
|
private readonly int _serverPort = 42010;
|
|
private readonly int _sendIntervalMs = 1000;
|
|
|
|
public Func<PLCPacket>? GetPacketToSend;
|
|
|
|
public bool IsConnected => _isConnected;
|
|
public bool IsRunning => _isRunning;
|
|
|
|
public async Task<bool> 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);
|
|
}
|
|
}
|
|
} |