using Edge.Core.UniversalApi; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore; using MQTTnet.AspNetCore; using Edge.Core.Processor; using Edge.Core.Configuration; using Microsoft.Extensions.Logging; using Edge.Core; using Edge.Core.Processor.Dispatcher; namespace Edge.WebHost { public class AspNetCoreWebApiCommunicatorProvider : ICommunicationProvider { public static UniversalApiHub UniversalApiHub { get; private set; } private bool isWebHostStarted = false; private IHost webHost; private IServiceProvider appServices; private IEnumerable processors; public AspNetCoreWebApiCommunicatorProvider(IServiceProvider appServices) { this.appServices = appServices; } public void Dispose() { this.webHost?.Dispose(); this.webHost?.StopAsync().Wait(); } public IEnumerable GetApiDocuments() { var pDescriptors = this.processors.Select(p => p.ProcessorDescriptor()) .Where(d => d.UniversalApiInfos != null && d.UniversalApiInfos.Any()); return pDescriptors.SelectMany(pd => pd.UniversalApiInfos).Select(apiInfo => { var doc = new UniversalApiInfoDoc() { ProviderType = apiInfo.Processor.ProcessorDescriptor().DeviceHandlerOrApp.GetType().FullName //+ ", " + apiInfo.Processor.MetaConfigName , BaseCategory = apiInfo.ServiceApiInfo == null ? (apiInfo.PropertyApiInfo == null ? "Event" : "Property") : "Service", ProviderConfigName = apiInfo.Processor.MetaConfigName, Description = apiInfo.ApiAttribute.Description, InputParametersExampleJson = apiInfo.ApiAttribute.InputParametersExampleJson, //InputParameters = apiInfo.ServiceApiInfo == null ? // // for event, providing the EventDataType // (apiInfo.PropertyApiInfo == null ? // (apiInfo.EventApiInfo?.EventDataType?.ToString() ?? "") // : apiInfo.PropertyApiInfo.PropertyType.ToString()) : // // for service, providing the parameter list // apiInfo.ServiceApiInfo.GetParameters()?.Select(p => p.ParameterType.Name + " " + (p.Name ?? ""))?.Aggregate("", (acc, n) => (string.IsNullOrEmpty(acc) ? "" : (acc + ", ")) + n) ?? "", }; var rawTags = new Type[] { apiInfo.Processor.ProcessorDescriptor().DeviceHandlerOrApp.GetType() }.ExtractProcessorMetaDescriptor(true).FirstOrDefault()?.Tags; if (rawTags != null) { var localizedTags = new List(); foreach (var t in rawTags) localizedTags.Add(t.LocalizedContent("en")); doc.ProviderTags = localizedTags.ToArray(); } string apiName = null; string path = null; string[] inputParametersJsonSchemaStrings = null; string outputParameterJsonSchema = null; if (apiInfo.ServiceApiInfo != null) { apiName = apiInfo.ServiceApiInfo.Name; path = $"/u/?apitype=service&an={apiName}&pn={apiInfo.Processor.MetaConfigName}&en={apiInfo.Processor.ProcessorDescriptor().DeviceHandlerOrApp.GetType().FullName}"; inputParametersJsonSchemaStrings = apiInfo.ServiceApiInfo.ResolveParamsJsonSchemas().ToArray(); outputParameterJsonSchema = apiInfo.ServiceApiInfo.ReturnType.GetGenericArguments().FirstOrDefault()?.ResolveParamsJsonSchema(); } else if (apiInfo.PropertyApiInfo != null) { apiName = apiInfo.PropertyApiInfo.Name; path = $"/u/?apitype=property&an={apiName}&pn={apiInfo.Processor.MetaConfigName}&en={apiInfo.Processor.ProcessorDescriptor().DeviceHandlerOrApp.GetType().FullName}"; if (!apiInfo.PropertyApiInfo.CanWrite || apiInfo.PropertyApiInfo.SetMethod == null || (apiInfo.PropertyApiInfo.SetMethod?.IsPrivate ?? false)) { // indicates this Property is read-only inputParametersJsonSchemaStrings = null; } else inputParametersJsonSchemaStrings = new string[] { apiInfo.PropertyApiInfo.ResolveParamsJsonSchemas() }; outputParameterJsonSchema = apiInfo.PropertyApiInfo.ResolveParamsJsonSchemas(); } else if (apiInfo.EventApiInfo != null) { outputParameterJsonSchema = apiInfo.EventApiInfo.EventDataType?.ResolveParamsJsonSchema(); } doc.ApiName = apiName; doc.Path = path; doc.InputParametersJsonSchemaStrings = inputParametersJsonSchemaStrings; doc.InputParametersExampleJson = doc.InputParametersExampleJson ?? inputParametersJsonSchemaStrings?.GenerateParametersJsonSample(); doc.OutputParametersJsonSchema = outputParameterJsonSchema; doc.OutputParametersExampleJson = doc.OutputParametersExampleJson ?? outputParameterJsonSchema?.GenerateParameterJsonSample(); return doc; }); return Enumerable.Empty(); //throw new NotImplementedException(); } public Task RouteEventAsync(IProcessor eventSource, EventDescriptor eventDescriptor) { //does not support event yet. return Task.FromResult(false); } public async Task SetupAsync(IEnumerable processors) { UniversalApiHub = this.appServices.GetRequiredService(); this.processors = processors; if (this.isWebHostStarted) return; var configurator = this.appServices.GetService(); var httpListeningIp = configurator.MetaConfiguration.Parameter?.FirstOrDefault(p => p.Name == "WebHostHttpListeningIp")?.Value ?? "*"; var httpListeningPort = configurator.MetaConfiguration.Parameter?.FirstOrDefault(p => p.Name == "WebHostHttpListeningPort")?.Value ?? "8384"; var mqttServerTcpListeningIp = configurator.MetaConfiguration.Parameter?.FirstOrDefault(p => p.Name == "MqttServerTcpListeningIp")?.Value ?? "*"; var mqttServerTcpListeningPort = configurator.MetaConfiguration.Parameter?.FirstOrDefault(p => p.Name == "MqttServerTcpListeningPort")?.Value ?? "8388"; this.webHost = CreateHostBuilder( new string[] { "%LAUNCHER_ARGS%" }, httpListeningIp, int.Parse(httpListeningPort), mqttServerTcpListeningIp, int.Parse(mqttServerTcpListeningPort)).Build(); await this.webHost.StartAsync(); this.isWebHostStarted = true; } private IHostBuilder CreateHostBuilder(string[] args, string httpListeningIp, int httpListeningPort, string mqttServerTcpListeningIp, int mqttServerTcpListeningPort ) => Host.CreateDefaultBuilder(args) .ConfigureServices((hostBuilderContext, aspNetCoreServices) => { aspNetCoreServices.Configure(option => { option.ShutdownTimeout = TimeSpan.FromSeconds(60); }); Func> pFactory = () => processors; aspNetCoreServices.AddSingleton(pFactory); aspNetCoreServices.AddSingleton(this.appServices.GetRequiredService()); aspNetCoreServices.AddSingleton(this.appServices.GetRequiredService()); aspNetCoreServices.AddSingleton(this.appServices.GetService()); }) //.ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Error)) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(o => { if (mqttServerTcpListeningIp == "*") o.ListenAnyIP(mqttServerTcpListeningPort, l => l.UseMqtt()); // MQTT pipeline else o.Listen(System.Net.IPAddress.Parse(mqttServerTcpListeningIp), mqttServerTcpListeningPort, l => l.UseMqtt()); // MQTT pipeline if (httpListeningIp == "*") o.ListenAnyIP(httpListeningPort);// Default HTTP pipeline else o.Listen(System.Net.IPAddress.Parse(httpListeningIp), httpListeningPort);// Default HTTP pipeline }); webBuilder.UseStartup(); //webBuilder.UseUrls(url); }); } }