|
@@ -0,0 +1,158 @@
|
|
|
+using ModbusSimulationProtocol.Data;
|
|
|
+using ModbusSimulationProtocol.Interface;
|
|
|
+using NModbus;
|
|
|
+using System.Net;
|
|
|
+using System.Net.Sockets;
|
|
|
+
|
|
|
+namespace ModbusSimulationProtocol.Services;
|
|
|
+
|
|
|
+public class ModbusSlaveService : IModbusSlaveService, IDisposable
|
|
|
+{
|
|
|
+ private readonly IModbusLogger? _logger;
|
|
|
+
|
|
|
+ private readonly ModbusFactory _factory;
|
|
|
+ private IModbusTcpSlaveNetwork? _network = null;
|
|
|
+
|
|
|
+ private bool disposedValue;
|
|
|
+
|
|
|
+ public ModbusSlaveService(IModbusLogger? logger)
|
|
|
+ {
|
|
|
+ _logger = logger;
|
|
|
+
|
|
|
+ _factory = new ModbusFactory(logger: _logger);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Initialize(string ip, ushort port)
|
|
|
+ {
|
|
|
+ ArgumentNullException.ThrowIfNullOrWhiteSpace(ip);
|
|
|
+ if (_network is not null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Information, "The slave network has been created.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ TcpListener listener = new TcpListener(IPAddress.Parse(ip), (int)port);
|
|
|
+ _network = _factory.CreateSlaveNetwork(listener);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Error, ex.ToString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ActivateNetwork()
|
|
|
+ {
|
|
|
+ if (_network is null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Warning, "Performs initializing before activating network.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Task.Run(async () =>
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Information, $"Starts async listening.");
|
|
|
+ await _network.ListenAsync();
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Error, ex.ToString());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void AddSlave(byte slaveId,
|
|
|
+ int coilCount, int discreteInputCount, int holdingRegisterCount, int inputRegisterCount)
|
|
|
+ {
|
|
|
+ if (_network is null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Warning, "Performs initializing before adding any slave.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var slave = _factory.CreateSlave(slaveId, new SlaveDataStore(coilCount, discreteInputCount, holdingRegisterCount, inputRegisterCount));
|
|
|
+ _network.AddSlave(slave);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Error, ex.ToString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void RemoveSlave(byte slaveId)
|
|
|
+ {
|
|
|
+ if (_network is null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Warning, "The slave network do not exist even.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_network.GetSlave(slaveId) is null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Warning, "The specified slave do not exist. Failed to remove.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _network.RemoveSlave(slaveId);
|
|
|
+ WriteLog(LoggingLevel.Information, $"The slave {slaveId} has been removed.");
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Error, ex.ToString());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public int GetConnectedMasterCount()
|
|
|
+ {
|
|
|
+ if (_network is null)
|
|
|
+ {
|
|
|
+ WriteLog(LoggingLevel.Warning, "The slave network do not exist even.");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return _network.Masters.Count;
|
|
|
+ }
|
|
|
+ private void WriteLog(LoggingLevel loggingLevel, string log)
|
|
|
+ {
|
|
|
+ _logger?.Log(loggingLevel, $"[{nameof(ModbusSlaveService)}]:{log}");
|
|
|
+ }
|
|
|
+
|
|
|
+ protected virtual void Dispose(bool disposing)
|
|
|
+ {
|
|
|
+ if (!disposedValue)
|
|
|
+ {
|
|
|
+ if (disposing)
|
|
|
+ {
|
|
|
+ // TODO: dispose managed state (managed objects)
|
|
|
+ _network?.Dispose();
|
|
|
+ _network = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
|
|
+ // TODO: set large fields to null
|
|
|
+ disposedValue = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
|
|
+ // ~ModbusSlaveService()
|
|
|
+ // {
|
|
|
+ // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
|
+ // Dispose(disposing: false);
|
|
|
+ // }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
|
+ Dispose(disposing: true);
|
|
|
+ GC.SuppressFinalize(this);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|