diff --git a/Platform.Communication.csproj b/Platform.Communication.csproj
new file mode 100644
index 0000000..9b91bba
--- /dev/null
+++ b/Platform.Communication.csproj
@@ -0,0 +1,31 @@
+
+
+
+ LinksPlatform's Platform.Communication Class Library
+ Konstantin Diachenko
+ Platform.Communication
+ 0.1.0
+ Konstantin Diachenko
+ netstandard2.0
+ Platform.Communication
+ Platform.Communication
+ Communication;Protocols;Gexf;Udp;UdpSender;UdpReceiver
+ https://raw.githubusercontent.com/linksplatform/Documentation/18469f4d033ee9a5b7b84caab9c585acab2ac519/doc/Avatar-rainbow-icon-64x64.png
+ https://github.com/linksplatform/Communication
+ LGPL-3.0-only
+ true
+ git
+ git://github.com/linksplatform/Communication
+ false
+ false
+ false
+ true
+ snupkg
+
+
+
+
+
+
+
+
diff --git a/Platform.Communication.sln b/Platform.Communication.sln
new file mode 100644
index 0000000..c4c8b76
--- /dev/null
+++ b/Platform.Communication.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29102.190
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Platform.Communication", "Platform.Communication.csproj", "{8B98EB46-0A79-45F7-AD16-CA7C17F1452C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8B98EB46-0A79-45F7-AD16-CA7C17F1452C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B98EB46-0A79-45F7-AD16-CA7C17F1452C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B98EB46-0A79-45F7-AD16-CA7C17F1452C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B98EB46-0A79-45F7-AD16-CA7C17F1452C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {98DDC0A8-01B8-4DF5-8EB7-74AA61EADDCE}
+ EndGlobalSection
+EndGlobal
diff --git a/Protocol/Gexf/Edge.cs b/Protocol/Gexf/Edge.cs
new file mode 100644
index 0000000..0898b8a
--- /dev/null
+++ b/Protocol/Gexf/Edge.cs
@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Platform.Communication.Protocol.Gexf
+{
+ public class Edge
+ {
+ public const string ElementName = "edge";
+ public const string IdAttributeName = "id";
+ public const string SourceAttributeName = "source";
+ public const string TargetAttributeName = "target";
+ public const string LabelAttributeName = "label";
+
+ [XmlAttribute(AttributeName = IdAttributeName)]
+ public long Id { get; set; }
+
+ [XmlAttribute(AttributeName = SourceAttributeName)]
+ public long Source { get; set; }
+
+ [XmlAttribute(AttributeName = TargetAttributeName)]
+ public long Target { get; set; }
+
+ [XmlAttribute(AttributeName = LabelAttributeName)]
+ public string Label { get; set; }
+
+ public void WriteXml(XmlWriter writer) => WriteXml(writer, Id, Source, Target, Label);
+
+ public static void WriteXml(XmlWriter writer, long id, long sourceNodeId, long targetNodeId, string label = null)
+ {
+ //
+ writer.WriteStartElement(ElementName);
+
+ writer.WriteAttributeString(IdAttributeName, id.ToString(CultureInfo.InvariantCulture));
+ writer.WriteAttributeString(SourceAttributeName, sourceNodeId.ToString(CultureInfo.InvariantCulture));
+ writer.WriteAttributeString(TargetAttributeName, targetNodeId.ToString(CultureInfo.InvariantCulture));
+ if (!string.IsNullOrWhiteSpace(label))
+ writer.WriteAttributeString(LabelAttributeName, label);
+
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/Protocol/Gexf/Enums.cs b/Protocol/Gexf/Enums.cs
new file mode 100644
index 0000000..e178508
--- /dev/null
+++ b/Protocol/Gexf/Enums.cs
@@ -0,0 +1,19 @@
+using System.Xml.Serialization;
+
+namespace Platform.Communication.Protocol.Gexf
+{
+ public enum GraphMode
+ {
+ [XmlEnum(Name = "static")]
+ Static,
+
+ [XmlEnum(Name = "dynamic")]
+ Dynamic
+ }
+
+ public enum GraphDefaultEdgeType
+ {
+ [XmlEnum(Name = "directed")]
+ Directed
+ }
+}
diff --git a/Protocol/Gexf/Gexf.cs b/Protocol/Gexf/Gexf.cs
new file mode 100644
index 0000000..7579cfc
--- /dev/null
+++ b/Protocol/Gexf/Gexf.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Platform.Communication.Protocol.Gexf
+{
+ [XmlRoot(ElementName = ElementName, Namespace = Namespace)]
+ public class Gexf
+ {
+ public const string ElementName = "gexf";
+ public const string Namespace = "http://www.gexf.net/1.2draft";
+ public const string VersionAttributeName = "version";
+ public const string GraphElementName = "graph";
+ public const string CurrentVersion = "1.2";
+
+ [XmlAttribute(AttributeName = VersionAttributeName)]
+ public string Version { get; set; }
+
+ [XmlElement(ElementName = GraphElementName)]
+ public Graph Graph { get; set; }
+
+ public Gexf()
+ {
+ Version = CurrentVersion;
+ Graph = new Graph();
+ }
+
+ public void WriteXml(XmlWriter writer)
+ {
+ void writeGraph() => Graph.WriteXml(writer);
+
+ WriteXml(writer, writeGraph, Version);
+ }
+
+ public static void WriteXml(XmlWriter writer, Action writeGraph, string version = CurrentVersion)
+ {
+ writer.WriteStartDocument();
+ writer.WriteStartElement(ElementName, Namespace);
+ writer.WriteAttributeString(VersionAttributeName, version);
+
+ writeGraph();
+
+ writer.WriteEndElement();
+ writer.WriteEndDocument();
+ }
+
+ public static void WriteXml(XmlWriter writer, Action writeNodes, Action writeEdges, string version = CurrentVersion,
+ GraphMode mode = GraphMode.Static, GraphDefaultEdgeType defaultEdgeType = GraphDefaultEdgeType.Directed)
+ {
+ WriteXml(writer, () => Graph.WriteXml(writer, writeNodes, writeEdges, mode, defaultEdgeType), version);
+ }
+ }
+}
diff --git a/Protocol/Gexf/Graph.cs b/Protocol/Gexf/Graph.cs
new file mode 100644
index 0000000..f3a16c9
--- /dev/null
+++ b/Protocol/Gexf/Graph.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Platform.Communication.Protocol.Gexf
+{
+ public class Graph
+ {
+ public const string ElementName = "graph";
+ public const string ModeAttributeName = "mode";
+ public const string DefaultEdgeTypeAttributeName = "defaultedgetype";
+ public const string NodesElementName = "nodes";
+ public const string NodeElementName = "node";
+ public const string EdgesElementName = "edges";
+ public const string EdgeElementName = "edge";
+
+ [XmlAttribute(AttributeName = ModeAttributeName)]
+ public GraphMode Mode { get; set; }
+
+ [XmlAttribute(AttributeName = DefaultEdgeTypeAttributeName)]
+ public GraphDefaultEdgeType DefaultEdgeType { get; set; }
+
+ [XmlArray(ElementName = NodesElementName)]
+ [XmlArrayItem(ElementName = NodeElementName)]
+ public List Nodes { get; set; }
+
+ [XmlArray(ElementName = EdgesElementName)]
+ [XmlArrayItem(ElementName = EdgeElementName)]
+ public List Edges { get; set; }
+
+ public Graph()
+ {
+ Nodes = new List();
+ Edges = new List();
+ }
+
+ public void WriteXml(XmlWriter writer)
+ {
+ void writeNodes()
+ {
+ for (var i = 0; i < Nodes.Count; i++)
+ Nodes[i].WriteXml(writer);
+ }
+
+ void writeEdges()
+ {
+ for (var i = 0; i < Edges.Count; i++)
+ Edges[i].WriteXml(writer);
+ }
+
+ WriteXml(writer, writeNodes, writeEdges, Mode, DefaultEdgeType);
+ }
+
+ public static void WriteXml(XmlWriter writer, Action writeNodes, Action writeEdges, GraphMode mode = GraphMode.Static, GraphDefaultEdgeType defaultEdgeType = GraphDefaultEdgeType.Directed)
+ {
+ writer.WriteStartElement(ElementName);
+
+ writer.WriteAttributeString(ModeAttributeName, mode.ToString().ToLower());
+ writer.WriteAttributeString(DefaultEdgeTypeAttributeName, defaultEdgeType.ToString().ToLower());
+
+ writer.WriteStartElement(NodesElementName);
+
+ writeNodes();
+
+ writer.WriteEndElement();
+
+ writer.WriteStartElement(EdgesElementName);
+
+ writeEdges();
+
+ writer.WriteEndElement();
+
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/Protocol/Gexf/Node.cs b/Protocol/Gexf/Node.cs
new file mode 100644
index 0000000..0f2dffd
--- /dev/null
+++ b/Protocol/Gexf/Node.cs
@@ -0,0 +1,32 @@
+using System.Globalization;
+using System.Xml;
+using System.Xml.Serialization;
+
+namespace Platform.Communication.Protocol.Gexf
+{
+ public class Node
+ {
+ public const string ElementName = "node";
+ public const string IdAttributeName = "id";
+ public const string LabelAttributeName = "label";
+
+ [XmlAttribute(AttributeName = IdAttributeName)]
+ public long Id { get; set; }
+
+ [XmlAttribute(AttributeName = LabelAttributeName)]
+ public string Label { get; set; }
+
+ public void WriteXml(XmlWriter writer) => WriteXml(writer, Id, Label);
+
+ public static void WriteXml(XmlWriter writer, long id, string label)
+ {
+ //
+ writer.WriteStartElement(ElementName);
+
+ writer.WriteAttributeString(IdAttributeName, id.ToString(CultureInfo.InvariantCulture));
+ writer.WriteAttributeString(LabelAttributeName, label);
+
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/Protocol/Udp/UdpClientExtensions.cs b/Protocol/Udp/UdpClientExtensions.cs
new file mode 100644
index 0000000..fefa79d
--- /dev/null
+++ b/Protocol/Udp/UdpClientExtensions.cs
@@ -0,0 +1,24 @@
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Platform.Threading;
+using Platform.Helpers;
+
+namespace Platform.Communication.Protocol.Udp
+{
+ public static class UdpClientExtensions
+ {
+ private static readonly Encoding DefaultEncoding = Singleton.Get(() => Encoding.GetEncoding(0));
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SendString(this UdpClient udp, IPEndPoint ipEndPoint, string message)
+ {
+ var bytes = DefaultEncoding.GetBytes(message);
+ return udp.SendAsync(bytes, bytes.Length, ipEndPoint).AwaitResult();
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string ReceiveString(this UdpClient udp) => DefaultEncoding.GetString(udp.ReceiveAsync().AwaitResult().Buffer);
+ }
+}
diff --git a/Protocol/Udp/UdpReceiver.cs b/Protocol/Udp/UdpReceiver.cs
new file mode 100644
index 0000000..7d591ed
--- /dev/null
+++ b/Protocol/Udp/UdpReceiver.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using Platform.Disposables;
+using Platform.Helpers;
+
+namespace Platform.Communication.Protocol.Udp
+{
+ public delegate void MessageHandlerCallback(string message);
+
+ ///
+ /// Представляет получателя сообщений по протоколу UDP.
+ ///
+ ///
+ /// TODO: Попробовать ThreadPool / Tasks
+ ///
+ public class UdpReceiver : DisposableBase //-V3073
+ {
+ private const int DefaultPort = 15000;
+
+ private bool _receiverRunning;
+ private Thread _thread;
+ private readonly int _listenPort;
+ private readonly UdpClient _udp;
+ private readonly MessageHandlerCallback _messageHandler;
+
+ public bool Available => _udp.Available > 0;
+
+ public UdpReceiver(int listenPort, bool autoStart, MessageHandlerCallback messageHandler)
+ {
+ _udp = new UdpClient(listenPort);
+ _listenPort = listenPort;
+ _messageHandler = messageHandler;
+
+ if (autoStart) Start();
+ }
+
+ public UdpReceiver(int listenPort, MessageHandlerCallback messageHandler)
+ : this(listenPort, true, messageHandler)
+ {
+ }
+
+ public UdpReceiver(MessageHandlerCallback messageHandler)
+ : this(DefaultPort, true, messageHandler)
+ {
+ }
+
+ public UdpReceiver()
+ : this(DefaultPort, true, message => { })
+ {
+ }
+
+ public void Start()
+ {
+ if (!_receiverRunning && _thread == null)
+ {
+ _receiverRunning = true;
+ _thread = new Thread(Receiver);
+ _thread.Start();
+ }
+ }
+
+ public void Stop()
+ {
+ if (_receiverRunning && _thread != null)
+ {
+ _receiverRunning = false;
+
+ // Send Packet to itself to switch Receiver from Receiving.
+ // TODO: Test new stopper
+ var loopback = new IPEndPoint(IPAddress.Loopback, _listenPort);
+ new UdpClient().SendAsync(new byte[0], 0, loopback).ContinueWith(Disposable.DisposeIfDisposable);
+
+ _thread.Join();
+ _thread = null;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public string Receive() => _udp.ReceiveString();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ReceiveAndHandle() => _messageHandler(Receive());
+
+ // Функция извлекающая пришедшие сообщения
+ // и работающая в отдельном потоке.
+ private void Receiver()
+ {
+ while (_receiverRunning)
+ {
+ try { ReceiveAndHandle(); }
+ catch (Exception exception)
+ {
+ Global.OnIgnoredException(exception);
+ }
+ }
+ }
+
+ protected override void DisposeCore(bool manual, bool wasDisposed)
+ {
+ if (!wasDisposed)
+ Stop();
+ Disposable.TryDispose(_udp);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Protocol/Udp/UdpSender.cs b/Protocol/Udp/UdpSender.cs
new file mode 100644
index 0000000..db1ca49
--- /dev/null
+++ b/Protocol/Udp/UdpSender.cs
@@ -0,0 +1,42 @@
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.CompilerServices;
+using Platform.Disposables;
+
+namespace Platform.Communication.Protocol.Udp
+{
+ ///
+ /// Представляет отправителя сообщений по протоколу UDP.
+ ///
+ public class UdpSender : DisposableBase //-V3073
+ {
+ private readonly UdpClient _udp;
+ private readonly IPEndPoint _ipendpoint;
+
+ public UdpSender(IPEndPoint ipendpoint)
+ {
+ _udp = new UdpClient();
+ _ipendpoint = ipendpoint;
+ }
+
+ public UdpSender(IPAddress address, int port)
+ : this(new IPEndPoint(address, port))
+ {
+ }
+
+ public UdpSender(string hostname, int port)
+ : this(IPAddress.Parse(hostname), port)
+ {
+ }
+
+ public UdpSender(int port)
+ : this(IPAddress.Loopback, port)
+ {
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Send(string message) => _udp.SendString(_ipendpoint, message);
+
+ protected override void DisposeCore(bool manual, bool wasDisposed) => Disposable.TryDispose(_udp);
+ }
+}
\ No newline at end of file
diff --git a/push-nuget.bat b/push-nuget.bat
new file mode 100644
index 0000000..4ab67b5
--- /dev/null
+++ b/push-nuget.bat
@@ -0,0 +1,6 @@
+dotnet pack -c Release
+cd bin\Release\
+nuget push -Source https://api.nuget.org/v3/index.json *.nupkg
+del *.nupkg
+del *.snupkg
+cd ..\..
\ No newline at end of file