using ProtocalGeneral; namespace FinsTcp; public class Fins_Tcp : IDisposable { private FinsTcpBase? _Service; private ITcpConnectNority? _tcpConnect; private string _IP = string.Empty; private int _port = 0; private Timer? _reconnectTimer; private Timer? _heartBeatTimer; private volatile ushort _heartBeatMissCount = 0; private ushort _maxHeartBeatMissCount = 5; private Func? _heartBeatCallBack; public bool Initialize(ITcpConnectNority? tcpConnect) { if (this._Service is not null) return false; this._Service = new FinsTcpBase(); this._tcpConnect = tcpConnect; return true; } public bool Connect(string IP, int port, Func func, ushort heartBeatMissCount = 5, short heartBeatInterval = 1000, int sendTimeout = 1000, int receiveTimeout = 1000) { if (this._Service is null) return false; this._IP = IP; this._port = port; try { if (!this._Service.Link(IP, port, sendTimeout, receiveTimeout)) return false; this._tcpConnect?.Connect(IP, port); this.StartHeartBeat(func, heartBeatMissCount, heartBeatInterval); } catch { return false; } return true; } private bool StartHeartBeat(Func func, ushort missCount = 5, short interval_ms = 1000) { if (this._heartBeatTimer is not null) return false; if (this._Service is null) return false; if (func is null) return false; this._maxHeartBeatMissCount = missCount; this._heartBeatCallBack = func; this._heartBeatTimer = new(HeatBeaterTimerCallBack, func, 0, interval_ms); return true; } private void HeatBeaterTimerCallBack(object? state) { if (this._heartBeatCallBack is null) return; if (this._heartBeatCallBack.Invoke()) return; if (++_heartBeatMissCount <= this._maxHeartBeatMissCount) return; this._heartBeatTimer?.Dispose(); this._tcpConnect?.DisConnect(this._IP, this._port); StartAutoReconnectTimer(); } private void StartAutoReconnectTimer() { this._heartBeatTimer?.Dispose(); this._heartBeatTimer = null; this._heartBeatMissCount = 0; this._reconnectTimer = new(ReconnectTimerCallBack, null, 0, 3000); } private void ReconnectTimerCallBack(object? state) { this._Service?.Close(); this._Service = null; this._heartBeatTimer?.Dispose(); this.Initialize(this._tcpConnect); if (!this.Connect(this._IP, this._port, this._heartBeatCallBack!, this._maxHeartBeatMissCount)) return; this._reconnectTimer?.Dispose(); } public bool GetData(string address, out T? data) { data = default; if (this._Service is null) return false; try { switch (Type.GetTypeCode(typeof(T))) { case TypeCode.Boolean: if (!this._Service.GetBitState(address, out short state)) return false; data = (T)(object)state; return true; case TypeCode.Int16: if (!this._Service.ReadWord(address, out short s)) return false; data = (T)(object)s; return true; case TypeCode.Int32: if (!this._Service.ReadInt32(address, out int i)) return false; data = (T)(object)i; return true; case TypeCode.Single: if (!this._Service.ReadReal(address, out float f)) return false; data = (T)(object)f; return true; default: return false; } } catch { return false; } } public bool SetData(string address, T data) where T : new() { if (this._Service is null) return false; try { return data switch { bool b => this._Service.SetBitState(address, b ? BitState.ON : BitState.OFF), short s => this._Service.WriteWord(address, s), int i => this._Service.WriteInt32(address, i), float f => this._Service.WriteReal(address, f), _ => false }; } catch { return false; } } #region Dispose private bool disposedValue; protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { this._Service?.Close(); this._Service = null; this._heartBeatTimer?.Dispose(); this._reconnectTimer?.Dispose(); this._tcpConnect?.DisConnect(this._IP, this._port); this._heartBeatMissCount = 0; } disposedValue = true; } } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } #endregion }