135 lines
3.3 KiB
C#
135 lines
3.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
namespace Barsik
|
|
{
|
|
class ModbusServer
|
|
{
|
|
private RegistersContainer registers;
|
|
|
|
public int Port { get; set; }
|
|
|
|
public ModbusServer(RegistersContainer registers)
|
|
{
|
|
this.registers = registers;
|
|
}
|
|
|
|
internal void Run()
|
|
{
|
|
var listener = new TcpListener(IPAddress.Any, Port);
|
|
listener.Start();
|
|
while (true)
|
|
{
|
|
var client = listener.AcceptTcpClient();
|
|
var worker = new ModbusServerWorker(registers, client);
|
|
Thread clientThread = new Thread(new ThreadStart(worker.Run));
|
|
clientThread.Start();
|
|
}
|
|
}
|
|
}
|
|
|
|
class ModbusServerWorker
|
|
{
|
|
const int RECEIVE_TIMEOUT = 10000; // 10 seconds
|
|
|
|
private RegistersContainer registers;
|
|
private TcpClient client;
|
|
public ModbusServerWorker(RegistersContainer registers, TcpClient client)
|
|
{
|
|
this.registers = registers;
|
|
this.client = client;
|
|
}
|
|
|
|
List<byte> received;
|
|
ManualResetEvent readCompleted;
|
|
int currentReceived;
|
|
NetworkStream stream;
|
|
byte[] buffer = new byte[256];
|
|
|
|
public void Run()
|
|
{
|
|
received = new List<byte>();
|
|
try
|
|
{
|
|
client.ReceiveTimeout = RECEIVE_TIMEOUT;
|
|
stream = client.GetStream();
|
|
readCompleted = new ManualResetEvent(false);
|
|
while (true)
|
|
{
|
|
readCompleted.Reset();
|
|
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.OnReadCompleted), this);
|
|
readCompleted.WaitOne();
|
|
if (currentReceived < 0)
|
|
{
|
|
client.Close();
|
|
return;
|
|
}
|
|
ProcessReceivedData();
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
client.Close();
|
|
}
|
|
}
|
|
|
|
private void ProcessReceivedData()
|
|
{
|
|
lock (received)
|
|
{
|
|
if (received.Count < 6) return;
|
|
int len = (((int)received[4] << 8) | (int)received[5]);
|
|
if (received.Count < 6 + len) return;
|
|
if (len < 2) throw new Exception("Wrong length");
|
|
int transactionId = (((int)received[0] << 8) | (int)received[1]);
|
|
byte unitId = received[6];
|
|
byte function = received[7];
|
|
List<byte> response = new List<byte>();
|
|
if (function == 0x03) // read holding registers
|
|
{
|
|
int addr = (((int)received[8] << 8) | (int)received[9]);
|
|
int amount = (((int)received[10] << 8) | (int)received[11]);
|
|
for (int a = addr; a < addr + amount; a++)
|
|
{
|
|
int value = registers[a];
|
|
response.Add((byte)((value >> 8) & 0xFF));
|
|
response.Add((byte)(value & 0xFF));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Unsupported function: " + function);
|
|
}
|
|
List<byte> fullResponse = new List<byte>();
|
|
fullResponse.Add(received[0]);
|
|
fullResponse.Add(received[1]); // transaction id
|
|
fullResponse.Add(received[2]);
|
|
fullResponse.Add(received[3]); // protocol id
|
|
int responseLength = 3 + response.Count;
|
|
fullResponse.Add((byte)((responseLength >> 8) & 0xFF));
|
|
fullResponse.Add((byte)((responseLength) & 0xFF));
|
|
fullResponse.Add(unitId);
|
|
fullResponse.Add(function);
|
|
fullResponse.Add((byte)response.Count);
|
|
fullResponse.AddRange(response);
|
|
received.RemoveRange(0, 6 + len);
|
|
stream.Write(fullResponse.ToArray(), 0, fullResponse.Count);
|
|
}
|
|
}
|
|
|
|
private void OnReadCompleted(IAsyncResult ar)
|
|
{
|
|
currentReceived = stream.EndRead(ar);
|
|
lock (received)
|
|
{
|
|
for (int i = 0; i < currentReceived; i++) received.Add(buffer[i]);
|
|
}
|
|
readCompleted.Set();
|
|
}
|
|
}
|
|
}
|