Files
Pilz/Pilz.Net/ConnectionManagerBase.cs
2024-08-22 14:38:32 +02:00

206 lines
6.2 KiB
C#

using Newtonsoft.Json.Linq;
using System.Data;
using System.Net;
namespace Pilz.Net;
public abstract class ConnectionManagerBase(int port)
{
private const int HEADER_LENGTH = 12;
private bool listening = false;
private readonly Dictionary<int, Dictionary<int, byte[]>> dicData = [];
public int Port { get; private set; } = port;
public bool UseAssemblyQualifiedName { get; set; } = false;
public event RetriveDataEventHandler? RetriveData;
public delegate void RetriveDataEventHandler(ConnectionManagerBase manager, string senderIP, string cmd, object content);
public bool IsListening
{
get => listening;
protected set => listening = value;
}
~ConnectionManagerBase()
{
Stop();
}
public abstract void Start();
public abstract void Stop();
protected abstract void SendData(IPEndPoint endPoint, byte[] data);
protected abstract int GetBufferSize();
public virtual void Send(string empfängerIP, string cmd)
{
Send(empfängerIP, cmd, string.Empty);
}
public virtual void Send(string empfängerIP, string cmd, string info)
{
Send(empfängerIP, cmd, (object)info);
}
private void RaiseRetriveData(string senderIP, string cmd, object content)
{
RetriveData?.Invoke(this, senderIP, cmd, content);
}
protected void ProcessRetrivedData(string senderIP, byte[] buf)
{
int readInteger(int index) => buf[index] << 24 | buf[index + 1] << 16 | buf[index + 2] << 8 | buf[index + 3];
int dataID = readInteger(0);
int packageID = readInteger(4);
int packageCount = readInteger(8);
bool resolveData = true;
// Remember data
byte[] data = buf.Skip(HEADER_LENGTH).ToArray();
Dictionary<int, byte[]> dicMyData;
if (dicData.ContainsKey(dataID))
{
dicMyData = dicData[dataID];
if (dicMyData.ContainsKey(packageID))
dicMyData[packageID] = data;
else
dicMyData.Add(packageID, data);
}
else
{
dicMyData = new Dictionary<int, byte[]>() { { packageID, data } };
dicData.Add(dataID, dicMyData);
}
if (dicMyData.Count < packageCount)
resolveData = false;
// Resolve Data
if (resolveData)
{
dicMyData ??= dicData[dataID];
var myData = new List<byte>();
foreach (var kvp in dicMyData.OrderBy(n => n.Key))
myData.AddRange(kvp.Value);
dicMyData.Remove(dataID);
object content = null;
string cmd = string.Empty;
try
{
var res = DecodeFromBytes(myData.ToArray());
cmd = res.cmd;
content = res.content;
}
catch (Exception)
{
}
RaiseRetriveData(senderIP, cmd, content);
}
}
private Random _Send_rnd = new();
public void Send(string empfängerIP, string cmd, object content)
{
var ep = new IPEndPoint(NetworkFeatures.GetIPFromHost(empfängerIP).MapToIPv4(), Port);
var finalBuffer = new List<byte>();
int maxBufferSize = GetBufferSize();
int maxDataSize = maxBufferSize - HEADER_LENGTH;
byte[] data = EncodeToBytes(cmd, content, UseAssemblyQualifiedName);
int dataID = _Send_rnd.Next();
// Some methods for later user
void send() => SendData(ep, finalBuffer.ToArray());
void addInteger(int value)
{
finalBuffer.Add((byte)(value >> 24 & 0xFF));
finalBuffer.Add((byte)(value >> 16 & 0xFF));
finalBuffer.Add((byte)(value >> 8 & 0xFF));
finalBuffer.Add((byte)(value & 0xFF));
};
void addHeader(int packageID, int packagesCount)
{
addInteger(dataID); // Data ID
addInteger(packageID); // Package ID
addInteger(packagesCount); // Packages Count
};
// Send data (this if statement and else content might be useless)
if (data.Length > maxDataSize)
{
int curIndex = 0;
int curID = 0;
int packagesCount = (int)Math.Round(Math.Ceiling(data.Length / (double)maxDataSize));
while (curIndex < data.Length)
{
finalBuffer.Clear();
addHeader(curID, packagesCount);
for (int i = 1, loopTo = maxDataSize; i <= loopTo; i++)
{
if (curIndex < data.Length)
{
finalBuffer.Add(data[curIndex]);
curIndex += 1;
}
}
send();
curID += 1;
}
}
else
{
addHeader(0, 1);
finalBuffer.AddRange(data);
send();
}
}
private static byte[] EncodeToBytes(string cmd, object content, bool useAssemblyQualifiedName)
{
var ms = new MemoryStream();
var bw = new BinaryWriter(ms);
var obj = new JObject
{
// Write header
["Cmd"] = cmd,
["ContentType"] = useAssemblyQualifiedName ? (content?.GetType()?.AssemblyQualifiedName) : (content?.GetType()?.ToString()),
// Content
["Content"] = JToken.FromObject(content)
};
// Write Json to MemoryStream
bw.Write(System.Text.Encoding.Default.GetBytes(obj.ToString()));
// Get Buffer Bytes
byte[] buf = ms.ToArray();
ms.Close();
return buf;
}
private static (string cmd, object content) DecodeFromBytes(byte[] buf)
{
string contentstring = System.Text.Encoding.Default.GetString(buf);
object content = null;
var contentobj = JObject.Parse(contentstring);
string cmd = (string)contentobj["Cmd"];
string contenttypestring = (string)contentobj["ContentType"];
var contentlinq = contentobj["Content"];
if (!string.IsNullOrEmpty(contenttypestring))
{
var contenttype = Type.GetType(contenttypestring);
content = contentlinq.ToObject(contenttype);
}
return (cmd, content);
}
}