using Edge.Core.Processor.Dispatcher.Attributes;
using Edge.Core.Processor.Dispatcher;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using Edge.Core.UniversalApi;
using Edge.Core.Processor.Communicator;
using Newtonsoft.Json.Schema.Generation;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
namespace Edge.Core.Processor
{
public enum ProcessorType
{
///
/// used to communciator with a device, must have a communicator underlying.
///
DeviceProcessor,
///
///
///
Application
}
public class ProcessorDescriptor
{
public IProcessor Processor { get; }
///
/// Gets the type of the Processor
///
public ProcessorType ProcessorType { get; }
///
/// will be the DeviceHandler instance if processor type is Device Processor.
/// will be the AppProcessor instance if processor type is IAppProcessor.
///
public object DeviceHandlerOrApp { get; }
///
/// Gets all the methodInfo for functions marked with UnversalApi attribute.
///
public IEnumerable UniversalApiInfos { get; }
///
/// will be null if processor is type AppProcessor.
///
public object DeviceProcessorContext { get; }
///
/// will be null if processor is type Application.
///
public object DeviceProcessorCommunicator { get; }
public ProcessorDescriptor(IProcessor processor, ProcessorType processorType,
object deviceHandlerOrApp,
IEnumerable universalApiInfos,
object deviceProcessorContext,
object deviceProcessorCommunicator)
{
this.Processor = processor;
this.ProcessorType = processorType;
this.DeviceProcessorContext = deviceProcessorContext;
this.DeviceProcessorCommunicator = deviceProcessorCommunicator;
this.DeviceHandlerOrApp = deviceHandlerOrApp;
this.UniversalApiInfos = universalApiInfos;
}
}
public class UniversalEventInfo
{
public UniversalEventInfo(string eventName, Type eventDataType)
{
this.EventName = eventName;
this.EventDataType = eventDataType;
}
public string EventName { get; private set; }
public Type EventDataType { get; private set; }
}
public class UniversalApiInfo
{
public IProcessor Processor { get; set; }
public UniversalApiInfo(MethodInfo serviceApiInfo, UniversalApiAttribute apiAttribute)
{
this.ServiceApiInfo = serviceApiInfo;
this.ApiAttribute = apiAttribute;
}
public UniversalApiInfo(PropertyInfo propertyApiInfo, UniversalApiAttribute apiAttribute)
{
this.PropertyApiInfo = propertyApiInfo;
this.ApiAttribute = apiAttribute;
}
public UniversalApiInfo(UniversalEventInfo eventApiInfo, UniversalApiAttribute apiAttribute)
{
this.EventApiInfo = eventApiInfo;
this.ApiAttribute = apiAttribute;
}
///
///
public MethodInfo ServiceApiInfo { get; private set; }
public PropertyInfo PropertyApiInfo { get; private set; }
public UniversalEventInfo EventApiInfo { get; private set; }
///
///
public UniversalApiAttribute ApiAttribute { get; private set; }
}
public class UniversalApiInfoDoc
{
///
/// the DeviceHanlder or AppProcessor type full name string.
///
public string ProviderType { get; set; }
public string[] ProviderTags { get; set; }
///
/// Name of the MetaConfig that instantiate the processor instance.
///
public string ProviderConfigName { get; set; }
///
/// like Service, Property, Event
///
public string BaseCategory { get; set; }
///
/// The Service function name, or Property name, or Event name.
///
public string ApiName { get; set; }
///
/// The unique string that used to locate the specific API endpoint in a Processor.
///
public string Path { get; set; }
public string InputParametersExampleJson { get; set; }
///
///
///
public string[] InputParametersJsonSchemaStrings { get; set; }
public string OutputParametersExampleJson { get; set; }
public string OutputParametersJsonSchema { get; set; }
public string Description { get; set; }
}
public static class ExtensionMethods
{
///
/// Get the ProcessorDescriptor for a processor.
///
///
/// ProcessorDescriptor
public static ProcessorDescriptor ProcessorDescriptor(this IProcessor processor)
{
if (processor == null)
throw new ArgumentNullException(nameof(processor));
if (processor is IAppProcessor application)
{
var apiInfos = GetUniversalApiInfos(application.GetType(), processor);
return new ProcessorDescriptor(processor,
ProcessorType.Application, processor, apiInfos, null, null);
}
else
{
dynamic p = processor;
var deviceProcessorHandler = p.Context.Handler;
Type handlerType = deviceProcessorHandler.GetType();
var methodInfos = GetUniversalApiInfos(handlerType, processor);
return new ProcessorDescriptor(processor,
ProcessorType.DeviceProcessor,
deviceProcessorHandler, methodInfos, p.Context, p.Context.Communicator);
}
}
private static IEnumerable GetUniversalApiInfos(Type handlerOrAppType, IProcessor processor)
{
var serviceUniversalApiInfos = handlerOrAppType.GetMethods()
.Where(m =>
m.CustomAttributes != null
&& m.CustomAttributes.Any(a => a.AttributeType
.IsAssignableFrom(typeof(UniversalApiAttribute)))
&& m.GetCustomAttribute().IgnoreApi == false
&& typeof(Task).IsAssignableFrom(m.ReturnType))
.Select(mi => new UniversalApiInfo(mi, mi.GetCustomAttributes().First())
{
Processor = processor
});
var eventUniversalApiAttributes = handlerOrAppType
.GetCustomAttributes().Where(att => att.IgnoreApi == false);
if (eventUniversalApiAttributes.Any()
&& eventUniversalApiAttributes.GroupBy(g => g.Name).Any(g => g.Count() >= 2))
throw new ArgumentOutOfRangeException("Multiple event UniversalApiAttributes with same Name: " +
(eventUniversalApiAttributes.GroupBy(g => g.Name).Where(g => g.Count() >= 2).First().Key ?? "")
+ " declared on type: " + handlerOrAppType.FullName + ", make sure the event name is unique on this type.");
var eventUniversalApiInfos = eventUniversalApiAttributes.Select(atti =>
new UniversalApiInfo(new UniversalEventInfo(atti.Name, atti.EventDataType), atti)
{
Processor = processor
});
var propertyUniversalApiInfos = handlerOrAppType.GetProperties()
.Where(p =>
p.CustomAttributes != null
&& p.CustomAttributes.Any(a => a.AttributeType
.IsAssignableFrom(typeof(UniversalApiAttribute)))
&& p.GetCustomAttribute().IgnoreApi == false)
.Select(p => new UniversalApiInfo(p, p.GetCustomAttributes().First())
{
Processor = processor
});
var apiInfos = serviceUniversalApiInfos.Concat(eventUniversalApiInfos).Concat(propertyUniversalApiInfos);
return apiInfos;
}
///
/// Filtering endpoint(either DeviceHandler or AppProcessor) from processors, and find those matched with its type Assignable for T.
///
///
///
/// processors that satisfy condition.
public static IEnumerable WithHandlerOrApp(this IEnumerable processors) where T : class
{
if (processors == null) throw new ArgumentNullException(nameof(processors));
return processors.Where(p => p.IsWithHandlerOrApp());
}
///
/// Determine the endpoint(either DeviceHandler or AppProcessor) from target processor satisfies its type Assignable for T.
///
///
///
///
public static bool IsWithHandlerOrApp(this IProcessor processor) where T : class
{
if (processor == null) throw new ArgumentNullException(nameof(processor));
var endpointType = processor.ProcessorDescriptor()?.DeviceHandlerOrApp?.GetType();
if (endpointType == null) return false;
return endpointType.IsAssignableFrom(typeof(T)) || typeof(T).IsAssignableFrom(endpointType);
}
///
/// Cast the DeviceHandler or AppProcessor to type T from the processors.
///
/// the type that will cast the DeviceHandler or AppProcessor to
///
/// DeviceHandlers or AppProcessors that casted to type T.
public static IEnumerable SelectHandlerOrAppThenCast(this IEnumerable processors) where T : class
{
if (processors == null) throw new ArgumentNullException(nameof(processors));
return processors.Select(p => p.SelectHandlerOrAppThenCast());
}
///
/// Cast the DeviceHandler or AppProcessor to type T from the processor.
///
/// the type that will cast the DeviceHandler or AppProcessor to
///
/// DeviceHandler or AppProcessor that casted to type T.
public static T SelectHandlerOrAppThenCast(this IProcessor processor) where T : class
{
if (processor == null) throw new ArgumentNullException(nameof(processor));
var casted = processor.ProcessorDescriptor().DeviceHandlerOrApp as T;
if (casted == null) throw new InvalidCastException();
return casted;
}
public static IEnumerable WithAttributeOrAll(this IEnumerable methodsOrCtors) where T : Attribute
{
var memberInfoWithAttri = methodsOrCtors.Where(mi => mi.GetCustomAttribute() != null);
if (memberInfoWithAttri.Any()) return memberInfoWithAttri;
return methodsOrCtors;
}
public static IEnumerable FilterByTags(this IEnumerable source, string[] tags)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (tags == null || !tags.Any() || tags.All(t => string.IsNullOrEmpty(t)))
return source;
else
{
return source.Where(d => d.ProviderTags != null && d.ProviderTags.Any()).Where(doc => tags.Intersect(doc.ProviderTags).Any());
//var filtered = new List();
//foreach (var doc in source.Where(d => d.ProviderTags != null && d.ProviderTags.Any()))
//{
// if (doc.ProviderTags.Any(dt => tags.Contains(dt)))
// filtered.Add(doc);
//}
//return filtered;
}
}
public static IEnumerable ExtractProcessorMetaDescriptor(
this IEnumerable targetEndPointTypes, bool includeSystemInternalComponent = false)
{
var descriptors = new List();
foreach (var endPointType in targetEndPointTypes)
{
var endpointTypeMetaPartsDescriptor = endPointType.GetCustomAttributes().FirstOrDefault();
if ((endpointTypeMetaPartsDescriptor?.IsSystemInternalComponent ?? false) && !includeSystemInternalComponent) continue;
var pmcDescriptor = new ProcessorMetaDescriptor
{
//Type = endPointType.GetInterfaces().Any(ti => ti.Name == typeof(IDeviceHandler<,>).Name) ?
// ProcessorTypeEnum.DeviceProcessor : ProcessorTypeEnum.AppProcessor,
Type = endPointType.GetInterfaces().Any(ti => ti.Name == typeof(IAppProcessor).Name) ?
ProcessorTypeEnum.AppProcessor : ProcessorTypeEnum.DeviceProcessor,
SourceEndpointFullTypeStr = endPointType.AssemblyQualifiedName,
Tags = endpointTypeMetaPartsDescriptor?.Tags,
DisplayName = endpointTypeMetaPartsDescriptor?.DisplayName,
Description = endpointTypeMetaPartsDescriptor?.Description
};
if (pmcDescriptor.Type == ProcessorTypeEnum.AppProcessor)
{
/*App only has one Parts*/
pmcDescriptor.MetaPartsGroupDescriptors = new List() {
new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.App,
MetaPartsDescriptors = new List()
{
new ProcessorMetaPartsDescriptor() {
FullTypeString = endPointType.AssemblyQualifiedName,
TypeString = endPointType.FullName,
DisplayName = endPointType.GetCustomAttribute()?.DisplayName,
Description =
endPointType.GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
endPointType.GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
}
}
}
};
}
else
{
var groupDescriptors = new List();
var deviceHandlerGroupDescriptor =
new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.DeviceHandler,
MetaPartsDescriptors = new List()
{
new ProcessorMetaPartsDescriptor() {
FullTypeString = endPointType.AssemblyQualifiedName,
TypeString = endPointType.FullName,
DisplayName = endPointType.GetCustomAttribute()?.DisplayName,
Description =
endPointType.GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
endPointType.GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
}
}
};
groupDescriptors.Add(deviceHandlerGroupDescriptor);
var metaPartsRequiredAttributes = endPointType.GetCustomAttributes();
var communicatorMetaPartsRequiredAttributes = metaPartsRequiredAttributes?.Where(at => at.RequiredPartsType?.GetInterfaces().Any(i => i.Name == typeof(ICommunicator<,>).Name) ?? false);
if (communicatorMetaPartsRequiredAttributes == null || !communicatorMetaPartsRequiredAttributes.Any())
{
var communicatorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.Communicator,
MetaPartsDescriptors = new List(){
new ProcessorMetaPartsDescriptor()
{
FullTypeString = typeof(ComPortCommunicator<>).AssemblyQualifiedName,
TypeString = typeof(ComPortCommunicator<>).FullName,
DisplayName = typeof(ComPortCommunicator<>).GetCustomAttribute()?.DisplayName,
Description =
typeof(ComPortCommunicator<>).GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
typeof(ComPortCommunicator<>).GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
},
new ProcessorMetaPartsDescriptor()
{
FullTypeString = typeof(TcpClientCommunicator<>).AssemblyQualifiedName,
TypeString = typeof(TcpClientCommunicator<>).FullName,
DisplayName = typeof(TcpClientCommunicator<>).GetCustomAttribute()?.DisplayName,
Description =
typeof(TcpClientCommunicator<>).GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
typeof(TcpClientCommunicator<>).GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
}
}
};
groupDescriptors.Add(communicatorGroupDescriptor);
}
else
{
groupDescriptors.Add(new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.Communicator,
MetaPartsDescriptors = communicatorMetaPartsRequiredAttributes.Select(att =>
new ProcessorMetaPartsDescriptor()
{
FullTypeString = att.RequiredPartsType.AssemblyQualifiedName,
TypeString = att.RequiredPartsType.FullName,
DisplayName = att.RequiredPartsType.GetCustomAttribute()?.DisplayName,
Description =
att.RequiredPartsType.GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
att.RequiredPartsType.GetConstructors().WithAttributeOrAll().Select(ctor => ctor.ResolveParamsJsonSchemas()).ToList(),
}).ToArray()
});
}
var deviceProcessorMetaPartsRequiredAttri = metaPartsRequiredAttributes?.FirstOrDefault(at => at.RequiredPartsType?.GetInterfaces().Any(i => i.Name == typeof(IDeviceProcessor<,>).Name) ?? false);
if (deviceProcessorMetaPartsRequiredAttri != null)
{
var specifiedProcessorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.DeviceProcessor,
MetaPartsDescriptors = new List(){
new ProcessorMetaPartsDescriptor()
{
FullTypeString = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.AssemblyQualifiedName,
TypeString = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.FullName,
DisplayName = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetCustomAttribute()?.DisplayName,
Description =
deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
} }
};
groupDescriptors.Add(specifiedProcessorGroupDescriptor);
}
else
{
var processorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
{
GroupType = ProcessorMetaPartsTypeEnum.DeviceProcessor,
MetaPartsDescriptors = new List(){
new ProcessorMetaPartsDescriptor()
{
FullTypeString = typeof(GenericDeviceProcessor<,>).AssemblyQualifiedName,
TypeString= typeof(GenericDeviceProcessor<,>).FullName,
DisplayName = typeof(GenericDeviceProcessor<,>).GetCustomAttribute()?.DisplayName,
Description =
typeof(GenericDeviceProcessor<,>).GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
typeof(GenericDeviceProcessor<,>).GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
},
new ProcessorMetaPartsDescriptor()
{
FullTypeString = typeof(HalfDuplexActivePollingDeviceProcessor<,>).AssemblyQualifiedName,
TypeString= typeof(HalfDuplexActivePollingDeviceProcessor<,>).FullName,
DisplayName = typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetCustomAttribute()?.DisplayName,
Description =
typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetCustomAttribute()?.Description,
ParametersJsonSchemaStrings =
typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetConstructors().WithAttributeOrAll().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
}
}
};
groupDescriptors.Add(processorGroupDescriptor);
}
pmcDescriptor.MetaPartsGroupDescriptors = groupDescriptors;
}
descriptors.Add(pmcDescriptor);
}
return descriptors;
}
///
/// call the fallback config resolve method from target metaparts type.
///
///
/// return true for called the method and updated the ParametersJsonArrayStr successfully,
/// false indicates no fallback config resolve method defined, and no update.
public static bool TryCallMetaPartsConfigCompatibilityMethodAndUpdate(this ProcessorMetaPartsConfig metaPartsConfig)
{
var metaPartsType = Type.GetType(metaPartsConfig.FullTypeString);// ObjectInstanceCreator.CurrentDomainProcessorEndPointTypes;
if (metaPartsType == null) return false;// throw new ArgumentException("Could not find any metaPartsType that has type full name to: " + metaPartsConfig.FullTypeString);
string methodName = "ResolveCtorMetaPartsConfigCompatibility";
var resolveCompatibleMethodInfo =
metaPartsType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
.Where(mi => mi.Name == methodName
&& mi.ReturnType == typeof(List