diff --git a/ZeroLevel.sln b/ZeroLevel.sln index fa44425..adb699f 100644 --- a/ZeroLevel.sln +++ b/ZeroLevel.sln @@ -27,7 +27,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Processor", "TestPipeLine\P EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Consumer", "TestPipeLine\Consumer\Consumer.csproj", "{931DEA89-42D1-4C06-9CB8-A3A0412093D6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.SQL", "ZeroLevel.SQL\ZeroLevel.SQL.csproj", "{D25EC1F0-3BD2-409C-8A01-8C8339D5835C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZeroLevel.SQL", "ZeroLevel.SQL\ZeroLevel.SQL.csproj", "{D25EC1F0-3BD2-409C-8A01-8C8339D5835C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroNetworkMonitor", "ZeroNetworkMonitor\ZeroNetworkMonitor.csproj", "{EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -171,6 +173,18 @@ Global {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x64.Build.0 = Release|Any CPU {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x86.ActiveCfg = Release|Any CPU {D25EC1F0-3BD2-409C-8A01-8C8339D5835C}.Release|x86.Build.0 = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|x64.ActiveCfg = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|x64.Build.0 = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|x86.ActiveCfg = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Debug|x86.Build.0 = Debug|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|Any CPU.Build.0 = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|x64.ActiveCfg = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|x64.Build.0 = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|x86.ActiveCfg = Release|Any CPU + {EECF6EA0-6D9C-4B69-9CA3-23357C04B84C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ZeroLevel/Models/ZeroServiceInfo.cs b/ZeroLevel/Models/ZeroServiceInfo.cs index ec1d8cf..98f18ca 100644 --- a/ZeroLevel/Models/ZeroServiceInfo.cs +++ b/ZeroLevel/Models/ZeroServiceInfo.cs @@ -40,6 +40,9 @@ namespace ZeroLevel public string Version { get; set; } /// /// Service port + /// + /// TODO move port out to new class for discovery service + /// /// [DataMember] public int Port { get; set; } diff --git a/ZeroLevel/Services/Network/Contracts/IServer.cs b/ZeroLevel/Services/Network/Contracts/IServer.cs index 54b091c..fd0a682 100644 --- a/ZeroLevel/Services/Network/Contracts/IServer.cs +++ b/ZeroLevel/Services/Network/Contracts/IServer.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using ZeroLevel.Network.SDL; namespace ZeroLevel.Network { @@ -26,6 +28,8 @@ namespace ZeroLevel.Network bool ContainsHandlerInbox(string inbox); bool ContainsRequestorInbox(string inbox); + IEnumerable CollectInboxInfo(); + event Action OnDisconnect; event Action OnConnect; } diff --git a/ZeroLevel/Services/Network/Contracts/IServiceRoutesStorage.cs b/ZeroLevel/Services/Network/Contracts/IServiceRoutesStorage.cs index 5f7719a..fba6050 100644 --- a/ZeroLevel/Services/Network/Contracts/IServiceRoutesStorage.cs +++ b/ZeroLevel/Services/Network/Contracts/IServiceRoutesStorage.cs @@ -16,6 +16,7 @@ namespace ZeroLevel.Network void Remove(IPEndPoint endpoint); IEnumerable> GetAll(); + IEnumerable GetKeys(); InvokeResult Get(string key); InvokeResult> GetAll(string key); diff --git a/ZeroLevel/Services/Network/Exchange.cs b/ZeroLevel/Services/Network/Exchange.cs index 343c145..95bef67 100644 --- a/ZeroLevel/Services/Network/Exchange.cs +++ b/ZeroLevel/Services/Network/Exchange.cs @@ -5,6 +5,7 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using ZeroLevel.Models; +using ZeroLevel.Network.SDL; using ZeroLevel.Services.Serialization; namespace ZeroLevel.Network @@ -654,17 +655,24 @@ namespace ZeroLevel.Network #region Host service public IRouter UseHost() { - return _cachee.GetServer(new IPEndPoint(IPAddress.Any, NetUtils.GetFreeTcpPort()), new Router()); + return MakeHost(new IPEndPoint(IPAddress.Any, NetUtils.GetFreeTcpPort())); } public IRouter UseHost(int port) { - return _cachee.GetServer(new IPEndPoint(IPAddress.Any, port), new Router()); + return MakeHost(new IPEndPoint(IPAddress.Any, port)); } public IRouter UseHost(IPEndPoint endpoint) { - return _cachee.GetServer(endpoint, new Router()); + return MakeHost(endpoint); + } + + private IRouter MakeHost(IPEndPoint endpoint) + { + var server = _cachee.GetServer(endpoint, new Router()); + server.RegisterInbox("__service_description__", _ => CollectServiceDescription()); + return server; } #endregion @@ -977,6 +985,22 @@ namespace ZeroLevel.Network } #endregion + private ServiceDescription CollectServiceDescription() + { + return new ServiceDescription + { + ServiceInfo = this._owner?.ServiceInfo, + Inboxes = _cachee.ServerList + .SelectMany(se => se + .CollectInboxInfo() + .Select(i => + { + i.Port = se.LocalEndpoint.Port; + return i; + })) + }; + } + public void Dispose() { if (_update_discovery_table_task != -1) diff --git a/ZeroLevel/Services/Network/Model/SDL/InboxKind.cs b/ZeroLevel/Services/Network/Model/SDL/InboxKind.cs new file mode 100644 index 0000000..181b7f4 --- /dev/null +++ b/ZeroLevel/Services/Network/Model/SDL/InboxKind.cs @@ -0,0 +1,10 @@ +namespace ZeroLevel.Network.SDL +{ + public enum InboxKind + { + Handler = 0, + HandlerNoArgs = 1, + Reqeustor = 2, + ReqeustorNoArgs = 3, + } +} diff --git a/ZeroLevel/Services/Network/Model/SDL/InboxServiceDescription.cs b/ZeroLevel/Services/Network/Model/SDL/InboxServiceDescription.cs new file mode 100644 index 0000000..f260999 --- /dev/null +++ b/ZeroLevel/Services/Network/Model/SDL/InboxServiceDescription.cs @@ -0,0 +1,50 @@ +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.Network.SDL +{ + public class InboxServiceDescription + : IBinarySerializable + { + public int Port { get; set; } + /// + /// Inbox name + /// + public string Name { get; set; } + /// + /// Invoke targer type name + /// + public string Target { get; set; } + /// + /// Inbox kind (handler or requestor) + /// + public InboxKind InboxKind { get; set; } + /// + /// Inbox Incoming data type + /// + public InboxType IncomingType { get; set; } + /// + /// Inbox Outcoming data type + /// + public InboxType OutcomingType { get; set; } + + public void Deserialize(IBinaryReader reader) + { + this.Port = reader.ReadInt32(); + this.Name = reader.ReadString(); + this.Target = reader.ReadString(); + this.InboxKind = (InboxKind)reader.ReadInt32(); + this.IncomingType = reader.Read(); + this.OutcomingType = reader.Read(); + } + + public void Serialize(IBinaryWriter writer) + { + writer.WriteInt32(this.Port); + writer.WriteString(this.Name); + writer.WriteString(this.Target); + writer.WriteInt32((int)this.InboxKind); + writer.Write(this.IncomingType); + writer.Write(this.OutcomingType); + } + } +} diff --git a/ZeroLevel/Services/Network/Model/SDL/InboxType.cs b/ZeroLevel/Services/Network/Model/SDL/InboxType.cs new file mode 100644 index 0000000..03d5cfc --- /dev/null +++ b/ZeroLevel/Services/Network/Model/SDL/InboxType.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.Network.SDL +{ + public class InboxType + : IBinarySerializable + { + /// + /// Type name + /// + public string Name { get; set; } + /// + /// Type fields (if composite type), top only + /// + public Dictionary Fields { get; set; } + + public void Deserialize(IBinaryReader reader) + { + this.Name = reader.ReadString(); + this.Fields = reader.ReadDictionary(); + } + + public void Serialize(IBinaryWriter writer) + { + writer.WriteString(this.Name); + writer.WriteDictionary(this.Fields); + } + } +} diff --git a/ZeroLevel/Services/Network/Model/SDL/ServiceDescription.cs b/ZeroLevel/Services/Network/Model/SDL/ServiceDescription.cs new file mode 100644 index 0000000..cb3bf81 --- /dev/null +++ b/ZeroLevel/Services/Network/Model/SDL/ServiceDescription.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.Network.SDL +{ + public class ServiceDescription + : IBinarySerializable + { + public ZeroServiceInfo ServiceInfo { get; set; } + public IEnumerable Inboxes { get; set; } + + public void Deserialize(IBinaryReader reader) + { + this.ServiceInfo = reader.Read(); + this.Inboxes = reader.ReadCollection(); + } + + public void Serialize(IBinaryWriter writer) + { + writer.Write(this.ServiceInfo); + writer.WriteCollection(this.Inboxes); + } + } +} diff --git a/ZeroLevel/Services/Network/SocketServer.cs b/ZeroLevel/Services/Network/SocketServer.cs index 0305259..b3685de 100644 --- a/ZeroLevel/Services/Network/SocketServer.cs +++ b/ZeroLevel/Services/Network/SocketServer.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; +using ZeroLevel.Network.SDL; namespace ZeroLevel.Network { @@ -143,6 +144,7 @@ namespace ZeroLevel.Network public bool ContainsInbox(string inbox) => _router.ContainsInbox(inbox); public bool ContainsHandlerInbox(string inbox) => _router.ContainsHandlerInbox(inbox); public bool ContainsRequestorInbox(string inbox) => _router.ContainsRequestorInbox(inbox); + public IEnumerable CollectInboxInfo() => _router.CollectInboxInfo(); #endregion } } diff --git a/ZeroLevel/Services/Network/Utils/Router.cs b/ZeroLevel/Services/Network/Utils/Router.cs index 2fa8116..37ba76b 100644 --- a/ZeroLevel/Services/Network/Utils/Router.cs +++ b/ZeroLevel/Services/Network/Utils/Router.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using ZeroLevel.Network.SDL; using ZeroLevel.Services.Invokation; using ZeroLevel.Services.Serialization; @@ -122,6 +123,55 @@ namespace ZeroLevel.Network } return null; } + + public InboxServiceDescription GetDescription(string name) + { + return new InboxServiceDescription + { + Name = name, + InboxKind = DetectKind(), + Target = _instance?.GetType()?.Name, + IncomingType = GetIncomingTypeDescription(), + OutcomingType = GetOutcomingTypeDescription() + }; + } + + private InboxType GetIncomingTypeDescription() + { + if (_typeReq == null) return null; + return new InboxType + { + Name = _typeReq.FullName, + Fields = _typeReq + .GetMembers(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance) + .Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property) + .Select(f => new KeyValuePair(f.Name, (f.MemberType == MemberTypes.Property) ? (f as PropertyInfo).PropertyType.FullName : (f as FieldInfo).FieldType.FullName)) + .ToDictionary(pair => pair.Key, pair => pair.Value) + }; + } + + private InboxType GetOutcomingTypeDescription() + { + if (_typeResp == null) return null; + return new InboxType + { + Name = _typeResp.FullName, + Fields = _typeResp + .GetMembers(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance) + .Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property) + .Select(f => new KeyValuePair(f.Name, (f.MemberType == MemberTypes.Property) ? (f as PropertyInfo).PropertyType.FullName : (f as FieldInfo).FieldType.FullName)) + .ToDictionary(pair => pair.Key, pair => pair.Value) + }; + } + + private InboxKind DetectKind() + { + if (_typeResp == null) + { + return _noArguments ? InboxKind.HandlerNoArgs : InboxKind.Handler; + } + return _noArguments ? InboxKind.ReqeustorNoArgs : InboxKind.Reqeustor; + } } private readonly Dictionary> _handlers = @@ -291,6 +341,23 @@ namespace ZeroLevel.Network return this; } #endregion + + public IEnumerable CollectInboxInfo() + { + var inboxes = new List(); + foreach (var handlers in _handlers) + { + foreach (var handler in handlers.Value) + { + inboxes.Add(handler.GetDescription(handlers.Key)); + } + } + foreach (var requestor in _requestors) + { + inboxes.Add(requestor.Value.GetDescription(requestor.Key)); + } + return inboxes; + } } internal sealed class NullRouter @@ -311,5 +378,6 @@ namespace ZeroLevel.Network public bool ContainsInbox(string inbox) => false; public bool ContainsHandlerInbox(string inbox) => false; public bool ContainsRequestorInbox(string inbox) => false; + public IEnumerable CollectInboxInfo() => Enumerable.Empty(); } } diff --git a/ZeroLevel/Services/Network/Utils/ServiceRouteStorage.cs b/ZeroLevel/Services/Network/Utils/ServiceRouteStorage.cs index fd835b9..7b2365c 100644 --- a/ZeroLevel/Services/Network/Utils/ServiceRouteStorage.cs +++ b/ZeroLevel/Services/Network/Utils/ServiceRouteStorage.cs @@ -242,13 +242,34 @@ namespace ZeroLevel.Network } #region GET + public IEnumerable GetKeys() + { + _lock.EnterReadLock(); + try + { + return _tableByKey.Select(pair => pair.Key).ToArray(); + } + finally + { + _lock.ExitReadLock(); + } + } + public InvokeResult Get(string key) { key = key.ToUpperInvariant(); - if (_tableByKey.ContainsKey(key)) + _lock.EnterReadLock(); + try { - if (_tableByKey[key].MoveNext()) - return InvokeResult.Succeeding(_tableByKey[key].Current); + if (_tableByKey.ContainsKey(key)) + { + if (_tableByKey[key].MoveNext()) + return InvokeResult.Succeeding(_tableByKey[key].Current); + } + } + finally + { + _lock.ExitReadLock(); } return InvokeResult.Fault($"No endpoints by key '{key}'"); } @@ -256,10 +277,18 @@ namespace ZeroLevel.Network public InvokeResult> GetAll(string key) { key = key.ToUpperInvariant(); - if (_tableByKey.ContainsKey(key)) + _lock.EnterReadLock(); + try { - if (_tableByKey[key].MoveNext()) - return InvokeResult.Succeeding(_tableByKey[key].GetCurrentSeq()); + if (_tableByKey.ContainsKey(key)) + { + if (_tableByKey[key].MoveNext()) + return InvokeResult.Succeeding(_tableByKey[key].GetCurrentSeq()); + } + } + finally + { + _lock.ExitReadLock(); } return InvokeResult.Fault>($"No endpoints by key '{key}'"); } @@ -267,46 +296,86 @@ namespace ZeroLevel.Network public IEnumerable> GetAll() { - return _tableByKey.SelectMany(pair => pair.Value.Source.Select(s => new KeyValuePair(pair.Key, s))); + _lock.EnterReadLock(); + try + { + return _tableByKey.SelectMany(pair => pair.Value.Source.Select(s => new KeyValuePair(pair.Key, s))); + } + finally + { + _lock.ExitReadLock(); + } } public InvokeResult GetByType(string type) { type = type.ToUpperInvariant(); - if (_tableByTypes.ContainsKey(type)) + _lock.EnterReadLock(); + try { - if (_tableByTypes[type].MoveNext()) - return InvokeResult.Succeeding(_tableByTypes[type].Current); + if (_tableByTypes.ContainsKey(type)) + { + if (_tableByTypes[type].MoveNext()) + return InvokeResult.Succeeding(_tableByTypes[type].Current); + } + } + finally + { + _lock.ExitReadLock(); } return InvokeResult.Fault($"No endpoints by type '{type}'"); } public InvokeResult> GetAllByType(string type) { type = type.ToUpperInvariant(); - if (_tableByTypes.ContainsKey(type)) + _lock.EnterReadLock(); + try + { + if (_tableByTypes.ContainsKey(type)) + { + if (_tableByTypes[type].MoveNext()) + return InvokeResult.Succeeding(_tableByTypes[type].GetCurrentSeq()); + } + } + finally { - if (_tableByTypes[type].MoveNext()) - return InvokeResult.Succeeding(_tableByTypes[type].GetCurrentSeq()); + _lock.ExitReadLock(); } return InvokeResult.Fault>($"No endpoints by type '{type}'"); } public InvokeResult GetByGroup(string group) { group = group.ToUpperInvariant(); - if (_tableByGroups.ContainsKey(group)) + _lock.EnterReadLock(); + try + { + if (_tableByGroups.ContainsKey(group)) + { + if (_tableByGroups[group].MoveNext()) + return InvokeResult.Succeeding(_tableByGroups[group].Current); + } + } + finally { - if (_tableByGroups[group].MoveNext()) - return InvokeResult.Succeeding(_tableByGroups[group].Current); + _lock.ExitReadLock(); } return InvokeResult.Fault($"No endpoints by group '{group}'"); } public InvokeResult> GetAllByGroup(string group) { group = group.ToUpperInvariant(); - if (_tableByGroups.ContainsKey(group)) + _lock.EnterReadLock(); + try + { + if (_tableByGroups.ContainsKey(group)) + { + if (_tableByGroups[group].MoveNext()) + return InvokeResult.Succeeding(_tableByGroups[group].GetCurrentSeq()); + } + } + finally { - if (_tableByGroups[group].MoveNext()) - return InvokeResult.Succeeding(_tableByGroups[group].GetCurrentSeq()); + _lock.ExitReadLock(); } return InvokeResult.Fault>($"No endpoints by group '{group}'"); } diff --git a/ZeroNetworkMonitor/App.config b/ZeroNetworkMonitor/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/ZeroNetworkMonitor/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ZeroNetworkMonitor/App.xaml b/ZeroNetworkMonitor/App.xaml new file mode 100644 index 0000000..df22562 --- /dev/null +++ b/ZeroNetworkMonitor/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/ZeroNetworkMonitor/App.xaml.cs b/ZeroNetworkMonitor/App.xaml.cs new file mode 100644 index 0000000..05a79aa --- /dev/null +++ b/ZeroNetworkMonitor/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace ZeroNetworkMonitor +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/ZeroNetworkMonitor/MainWindow.xaml b/ZeroNetworkMonitor/MainWindow.xaml new file mode 100644 index 0000000..5c0234a --- /dev/null +++ b/ZeroNetworkMonitor/MainWindow.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + +