ExtensionMethods.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. using Edge.Core.Processor.Dispatcher.Attributes;
  2. using Edge.Core.Processor.Dispatcher;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Data;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text;
  9. using System.Text.Json.Serialization;
  10. using System.Text.Json;
  11. using System.Threading.Tasks;
  12. using System.Xml.Linq;
  13. using Edge.Core.UniversalApi;
  14. using Edge.Core.Processor.Communicator;
  15. using Newtonsoft.Json.Schema.Generation;
  16. using Newtonsoft.Json.Linq;
  17. using Newtonsoft.Json.Schema;
  18. namespace Edge.Core.Processor
  19. {
  20. public enum ProcessorType
  21. {
  22. /// <summary>
  23. /// used to communciator with a device, must have a communicator underlying.
  24. /// </summary>
  25. DeviceProcessor,
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. Application
  30. }
  31. public class ProcessorDescriptor
  32. {
  33. public IProcessor Processor { get; }
  34. /// <summary>
  35. /// Gets the type of the Processor
  36. /// </summary>
  37. public ProcessorType ProcessorType { get; }
  38. /// <summary>
  39. /// will be the DeviceHandler instance if processor type is Device Processor.
  40. /// will be the AppProcessor instance if processor type is IAppProcessor.
  41. /// </summary>
  42. public object DeviceHandlerOrApp { get; }
  43. /// <summary>
  44. /// Gets all the methodInfo for functions marked with UnversalApi attribute.
  45. /// </summary>
  46. public IEnumerable<UniversalApiInfo> UniversalApiInfos { get; }
  47. /// <summary>
  48. /// will be null if processor is type AppProcessor.
  49. /// </summary>
  50. public object DeviceProcessorContext { get; }
  51. /// <summary>
  52. /// will be null if processor is type Application.
  53. /// </summary>
  54. public object DeviceProcessorCommunicator { get; }
  55. public ProcessorDescriptor(IProcessor processor, ProcessorType processorType,
  56. object deviceHandlerOrApp,
  57. IEnumerable<UniversalApiInfo> universalApiInfos,
  58. object deviceProcessorContext,
  59. object deviceProcessorCommunicator)
  60. {
  61. this.Processor = processor;
  62. this.ProcessorType = processorType;
  63. this.DeviceProcessorContext = deviceProcessorContext;
  64. this.DeviceProcessorCommunicator = deviceProcessorCommunicator;
  65. this.DeviceHandlerOrApp = deviceHandlerOrApp;
  66. this.UniversalApiInfos = universalApiInfos;
  67. }
  68. }
  69. public class UniversalEventInfo
  70. {
  71. public UniversalEventInfo(string eventName, Type eventDataType)
  72. {
  73. this.EventName = eventName;
  74. this.EventDataType = eventDataType;
  75. }
  76. public string EventName { get; private set; }
  77. public Type EventDataType { get; private set; }
  78. }
  79. public class UniversalApiInfo
  80. {
  81. public IProcessor Processor { get; set; }
  82. public UniversalApiInfo(MethodInfo serviceApiInfo, UniversalApiAttribute apiAttribute)
  83. {
  84. this.ServiceApiInfo = serviceApiInfo;
  85. this.ApiAttribute = apiAttribute;
  86. }
  87. public UniversalApiInfo(PropertyInfo propertyApiInfo, UniversalApiAttribute apiAttribute)
  88. {
  89. this.PropertyApiInfo = propertyApiInfo;
  90. this.ApiAttribute = apiAttribute;
  91. }
  92. public UniversalApiInfo(UniversalEventInfo eventApiInfo, UniversalApiAttribute apiAttribute)
  93. {
  94. this.EventApiInfo = eventApiInfo;
  95. this.ApiAttribute = apiAttribute;
  96. }
  97. /// <summary>
  98. /// </summary>
  99. public MethodInfo ServiceApiInfo { get; private set; }
  100. public PropertyInfo PropertyApiInfo { get; private set; }
  101. public UniversalEventInfo EventApiInfo { get; private set; }
  102. /// <summary>
  103. /// </summary>
  104. public UniversalApiAttribute ApiAttribute { get; private set; }
  105. }
  106. public class UniversalApiInfoDoc
  107. {
  108. /// <summary>
  109. /// the DeviceHanlder or AppProcessor type full name string.
  110. /// </summary>
  111. public string ProviderType { get; set; }
  112. public string[] ProviderTags { get; set; }
  113. /// <summary>
  114. /// Name of the MetaConfig that instantiate the processor instance.
  115. /// </summary>
  116. public string ProviderConfigName { get; set; }
  117. /// <summary>
  118. /// like Service, Property, Event
  119. /// </summary>
  120. public string BaseCategory { get; set; }
  121. /// <summary>
  122. /// The Service function name, or Property name, or Event name.
  123. /// </summary>
  124. public string ApiName { get; set; }
  125. /// <summary>
  126. /// The unique string that used to locate the specific API endpoint in a Processor.
  127. /// </summary>
  128. public string Path { get; set; }
  129. public string InputParametersExampleJson { get; set; }
  130. /// <summary>
  131. ///
  132. /// </summary>
  133. public string[] InputParametersJsonSchemaStrings { get; set; }
  134. public string OutputParametersExampleJson { get; set; }
  135. public string OutputParametersJsonSchema { get; set; }
  136. public string Description { get; set; }
  137. }
  138. public static class ExtensionMethods
  139. {
  140. /// <summary>
  141. /// Get the ProcessorDescriptor for a processor.
  142. /// </summary>
  143. /// <param name="processor"></param>
  144. /// <returns>ProcessorDescriptor</returns>
  145. public static ProcessorDescriptor ProcessorDescriptor(this IProcessor processor)
  146. {
  147. if (processor == null)
  148. throw new ArgumentNullException(nameof(processor));
  149. if (processor is IAppProcessor application)
  150. {
  151. var apiInfos = GetUniversalApiInfos(application.GetType(), processor);
  152. return new ProcessorDescriptor(processor,
  153. ProcessorType.Application, processor, apiInfos, null, null);
  154. }
  155. else
  156. {
  157. dynamic p = processor;
  158. var deviceProcessorHandler = p.Context.Handler;
  159. Type handlerType = deviceProcessorHandler.GetType();
  160. var methodInfos = GetUniversalApiInfos(handlerType, processor);
  161. return new ProcessorDescriptor(processor,
  162. ProcessorType.DeviceProcessor,
  163. deviceProcessorHandler, methodInfos, p.Context, p.Context.Communicator);
  164. }
  165. }
  166. private static IEnumerable<UniversalApiInfo> GetUniversalApiInfos(Type handlerOrAppType, IProcessor processor)
  167. {
  168. var serviceUniversalApiInfos = handlerOrAppType.GetMethods()
  169. .Where(m =>
  170. m.CustomAttributes != null
  171. && m.CustomAttributes.Any(a => a.AttributeType
  172. .IsAssignableFrom(typeof(UniversalApiAttribute)))
  173. && m.GetCustomAttribute<UniversalApiAttribute>().IgnoreApi == false
  174. && typeof(Task).IsAssignableFrom(m.ReturnType))
  175. .Select(mi => new UniversalApiInfo(mi, mi.GetCustomAttributes<UniversalApiAttribute>().First())
  176. {
  177. Processor = processor
  178. });
  179. var eventUniversalApiAttributes = handlerOrAppType
  180. .GetCustomAttributes<UniversalApiAttribute>().Where(att => att.IgnoreApi == false);
  181. if (eventUniversalApiAttributes.Any()
  182. && eventUniversalApiAttributes.GroupBy(g => g.Name).Any(g => g.Count() >= 2))
  183. throw new ArgumentOutOfRangeException("Multiple event UniversalApiAttributes with same Name: " +
  184. (eventUniversalApiAttributes.GroupBy(g => g.Name).Where(g => g.Count() >= 2).First().Key ?? "")
  185. + " declared on type: " + handlerOrAppType.FullName + ", make sure the event name is unique on this type.");
  186. var eventUniversalApiInfos = eventUniversalApiAttributes.Select(atti =>
  187. new UniversalApiInfo(new UniversalEventInfo(atti.Name, atti.EventDataType), atti)
  188. {
  189. Processor = processor
  190. });
  191. var propertyUniversalApiInfos = handlerOrAppType.GetProperties()
  192. .Where(p =>
  193. p.CustomAttributes != null
  194. && p.CustomAttributes.Any(a => a.AttributeType
  195. .IsAssignableFrom(typeof(UniversalApiAttribute)))
  196. && p.GetCustomAttribute<UniversalApiAttribute>().IgnoreApi == false)
  197. .Select(p => new UniversalApiInfo(p, p.GetCustomAttributes<UniversalApiAttribute>().First())
  198. {
  199. Processor = processor
  200. });
  201. var apiInfos = serviceUniversalApiInfos.Concat(eventUniversalApiInfos).Concat(propertyUniversalApiInfos);
  202. return apiInfos;
  203. }
  204. /// <summary>
  205. /// Filtering endpoint(either DeviceHandler or AppProcessor) from processors, and find those matched with its type Assignable for T.
  206. /// </summary>
  207. /// <typeparam name="T"></typeparam>
  208. /// <param name="processors"></param>
  209. /// <returns>processors that satisfy condition.</returns>
  210. public static IEnumerable<IProcessor> WithHandlerOrApp<T>(this IEnumerable<IProcessor> processors) where T : class
  211. {
  212. if (processors == null) throw new ArgumentNullException(nameof(processors));
  213. return processors.Where(p => p.IsWithHandlerOrApp<T>());
  214. }
  215. /// <summary>
  216. /// Determine the endpoint(either DeviceHandler or AppProcessor) from target processor satisfies its type Assignable for T.
  217. /// </summary>
  218. /// <typeparam name="T"></typeparam>
  219. /// <param name="processor"></param>
  220. /// <returns></returns>
  221. public static bool IsWithHandlerOrApp<T>(this IProcessor processor) where T : class
  222. {
  223. if (processor == null) throw new ArgumentNullException(nameof(processor));
  224. var endpointType = processor.ProcessorDescriptor()?.DeviceHandlerOrApp?.GetType();
  225. if (endpointType == null) return false;
  226. return endpointType.IsAssignableFrom(typeof(T)) || typeof(T).IsAssignableFrom(endpointType);
  227. }
  228. /// <summary>
  229. /// Cast the DeviceHandler or AppProcessor to type T from the processors.
  230. /// </summary>
  231. /// <typeparam name="T">the type that will cast the DeviceHandler or AppProcessor to</typeparam>
  232. /// <param name="processors"></param>
  233. /// <returns>DeviceHandlers or AppProcessors that casted to type T.</returns>
  234. public static IEnumerable<T> SelectHandlerOrAppThenCast<T>(this IEnumerable<IProcessor> processors) where T : class
  235. {
  236. if (processors == null) throw new ArgumentNullException(nameof(processors));
  237. return processors.Select(p => p.SelectHandlerOrAppThenCast<T>());
  238. }
  239. /// <summary>
  240. /// Cast the DeviceHandler or AppProcessor to type T from the processor.
  241. /// </summary>
  242. /// <typeparam name="T">the type that will cast the DeviceHandler or AppProcessor to</typeparam>
  243. /// <param name="processor"></param>
  244. /// <returns>DeviceHandler or AppProcessor that casted to type T.</returns>
  245. public static T SelectHandlerOrAppThenCast<T>(this IProcessor processor) where T : class
  246. {
  247. if (processor == null) throw new ArgumentNullException(nameof(processor));
  248. var casted = processor.ProcessorDescriptor().DeviceHandlerOrApp as T;
  249. if (casted == null) throw new InvalidCastException();
  250. return casted;
  251. }
  252. public static IEnumerable<MethodBase> WithAttributeOrAll<T>(this IEnumerable<MethodBase> methodsOrCtors) where T : Attribute
  253. {
  254. var memberInfoWithAttri = methodsOrCtors.Where(mi => mi.GetCustomAttribute<T>() != null);
  255. if (memberInfoWithAttri.Any()) return memberInfoWithAttri;
  256. return methodsOrCtors;
  257. }
  258. public static IEnumerable<UniversalApiInfoDoc> FilterByTags(this IEnumerable<UniversalApiInfoDoc> source, string[] tags)
  259. {
  260. if (source == null) throw new ArgumentNullException(nameof(source));
  261. if (tags == null || !tags.Any() || tags.All(t => string.IsNullOrEmpty(t)))
  262. return source;
  263. else
  264. {
  265. return source.Where(d => d.ProviderTags != null && d.ProviderTags.Any()).Where(doc => tags.Intersect(doc.ProviderTags).Any());
  266. //var filtered = new List<UniversalApiInfoDoc>();
  267. //foreach (var doc in source.Where(d => d.ProviderTags != null && d.ProviderTags.Any()))
  268. //{
  269. // if (doc.ProviderTags.Any(dt => tags.Contains(dt)))
  270. // filtered.Add(doc);
  271. //}
  272. //return filtered;
  273. }
  274. }
  275. public static IEnumerable<ProcessorMetaDescriptor> ExtractProcessorMetaDescriptor(
  276. this IEnumerable<Type> targetEndPointTypes, bool includeSystemInternalComponent = false)
  277. {
  278. var descriptors = new List<ProcessorMetaDescriptor>();
  279. foreach (var endPointType in targetEndPointTypes)
  280. {
  281. var endpointTypeMetaPartsDescriptor = endPointType.GetCustomAttributes<MetaPartsDescriptor>().FirstOrDefault();
  282. if ((endpointTypeMetaPartsDescriptor?.IsSystemInternalComponent ?? false) && !includeSystemInternalComponent) continue;
  283. var pmcDescriptor = new ProcessorMetaDescriptor
  284. {
  285. //Type = endPointType.GetInterfaces().Any(ti => ti.Name == typeof(IDeviceHandler<,>).Name) ?
  286. // ProcessorTypeEnum.DeviceProcessor : ProcessorTypeEnum.AppProcessor,
  287. Type = endPointType.GetInterfaces().Any(ti => ti.Name == typeof(IAppProcessor).Name) ?
  288. ProcessorTypeEnum.AppProcessor : ProcessorTypeEnum.DeviceProcessor,
  289. SourceEndpointFullTypeStr = endPointType.AssemblyQualifiedName,
  290. Tags = endpointTypeMetaPartsDescriptor?.Tags,
  291. DisplayName = endpointTypeMetaPartsDescriptor?.DisplayName,
  292. Description = endpointTypeMetaPartsDescriptor?.Description
  293. };
  294. if (pmcDescriptor.Type == ProcessorTypeEnum.AppProcessor)
  295. {
  296. /*App only has one Parts*/
  297. pmcDescriptor.MetaPartsGroupDescriptors = new List<ProcessorMetaPartsGroupDescriptor>() {
  298. new ProcessorMetaPartsGroupDescriptor()
  299. {
  300. GroupType = ProcessorMetaPartsTypeEnum.App,
  301. MetaPartsDescriptors = new List<ProcessorMetaPartsDescriptor>()
  302. {
  303. new ProcessorMetaPartsDescriptor() {
  304. FullTypeString = endPointType.AssemblyQualifiedName,
  305. TypeString = endPointType.FullName,
  306. DisplayName = endPointType.GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  307. Description =
  308. endPointType.GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  309. ParametersJsonSchemaStrings =
  310. endPointType.GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  311. }
  312. }
  313. }
  314. };
  315. }
  316. else
  317. {
  318. var groupDescriptors = new List<ProcessorMetaPartsGroupDescriptor>();
  319. var deviceHandlerGroupDescriptor =
  320. new ProcessorMetaPartsGroupDescriptor()
  321. {
  322. GroupType = ProcessorMetaPartsTypeEnum.DeviceHandler,
  323. MetaPartsDescriptors = new List<ProcessorMetaPartsDescriptor>()
  324. {
  325. new ProcessorMetaPartsDescriptor() {
  326. FullTypeString = endPointType.AssemblyQualifiedName,
  327. TypeString = endPointType.FullName,
  328. DisplayName = endPointType.GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  329. Description =
  330. endPointType.GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  331. ParametersJsonSchemaStrings =
  332. endPointType.GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  333. }
  334. }
  335. };
  336. groupDescriptors.Add(deviceHandlerGroupDescriptor);
  337. var metaPartsRequiredAttributes = endPointType.GetCustomAttributes<MetaPartsRequired>();
  338. var communicatorMetaPartsRequiredAttributes = metaPartsRequiredAttributes?.Where(at => at.RequiredPartsType?.GetInterfaces().Any(i => i.Name == typeof(ICommunicator<,>).Name) ?? false);
  339. if (communicatorMetaPartsRequiredAttributes == null || !communicatorMetaPartsRequiredAttributes.Any())
  340. {
  341. var communicatorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
  342. {
  343. GroupType = ProcessorMetaPartsTypeEnum.Communicator,
  344. MetaPartsDescriptors = new List<ProcessorMetaPartsDescriptor>(){
  345. new ProcessorMetaPartsDescriptor()
  346. {
  347. FullTypeString = typeof(ComPortCommunicator<>).AssemblyQualifiedName,
  348. TypeString = typeof(ComPortCommunicator<>).FullName,
  349. DisplayName = typeof(ComPortCommunicator<>).GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  350. Description =
  351. typeof(ComPortCommunicator<>).GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  352. ParametersJsonSchemaStrings =
  353. typeof(ComPortCommunicator<>).GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  354. },
  355. new ProcessorMetaPartsDescriptor()
  356. {
  357. FullTypeString = typeof(TcpClientCommunicator<>).AssemblyQualifiedName,
  358. TypeString = typeof(TcpClientCommunicator<>).FullName,
  359. DisplayName = typeof(TcpClientCommunicator<>).GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  360. Description =
  361. typeof(TcpClientCommunicator<>).GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  362. ParametersJsonSchemaStrings =
  363. typeof(TcpClientCommunicator<>).GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  364. }
  365. }
  366. };
  367. groupDescriptors.Add(communicatorGroupDescriptor);
  368. }
  369. else
  370. {
  371. groupDescriptors.Add(new ProcessorMetaPartsGroupDescriptor()
  372. {
  373. GroupType = ProcessorMetaPartsTypeEnum.Communicator,
  374. MetaPartsDescriptors = communicatorMetaPartsRequiredAttributes.Select(att =>
  375. new ProcessorMetaPartsDescriptor()
  376. {
  377. FullTypeString = att.RequiredPartsType.AssemblyQualifiedName,
  378. TypeString = att.RequiredPartsType.FullName,
  379. DisplayName = att.RequiredPartsType.GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  380. Description =
  381. att.RequiredPartsType.GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  382. ParametersJsonSchemaStrings =
  383. att.RequiredPartsType.GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor => ctor.ResolveParamsJsonSchemas()).ToList(),
  384. }).ToArray()
  385. });
  386. }
  387. var deviceProcessorMetaPartsRequiredAttri = metaPartsRequiredAttributes?.FirstOrDefault(at => at.RequiredPartsType?.GetInterfaces().Any(i => i.Name == typeof(IDeviceProcessor<,>).Name) ?? false);
  388. if (deviceProcessorMetaPartsRequiredAttri != null)
  389. {
  390. var specifiedProcessorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
  391. {
  392. GroupType = ProcessorMetaPartsTypeEnum.DeviceProcessor,
  393. MetaPartsDescriptors = new List<ProcessorMetaPartsDescriptor>(){
  394. new ProcessorMetaPartsDescriptor()
  395. {
  396. FullTypeString = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.AssemblyQualifiedName,
  397. TypeString = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.FullName,
  398. DisplayName = deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  399. Description =
  400. deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  401. ParametersJsonSchemaStrings =
  402. deviceProcessorMetaPartsRequiredAttri.RequiredPartsType.GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  403. } }
  404. };
  405. groupDescriptors.Add(specifiedProcessorGroupDescriptor);
  406. }
  407. else
  408. {
  409. var processorGroupDescriptor = new ProcessorMetaPartsGroupDescriptor()
  410. {
  411. GroupType = ProcessorMetaPartsTypeEnum.DeviceProcessor,
  412. MetaPartsDescriptors = new List<ProcessorMetaPartsDescriptor>(){
  413. new ProcessorMetaPartsDescriptor()
  414. {
  415. FullTypeString = typeof(GenericDeviceProcessor<,>).AssemblyQualifiedName,
  416. TypeString= typeof(GenericDeviceProcessor<,>).FullName,
  417. DisplayName = typeof(GenericDeviceProcessor<,>).GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  418. Description =
  419. typeof(GenericDeviceProcessor<,>).GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  420. ParametersJsonSchemaStrings =
  421. typeof(GenericDeviceProcessor<,>).GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  422. },
  423. new ProcessorMetaPartsDescriptor()
  424. {
  425. FullTypeString = typeof(HalfDuplexActivePollingDeviceProcessor<,>).AssemblyQualifiedName,
  426. TypeString= typeof(HalfDuplexActivePollingDeviceProcessor<,>).FullName,
  427. DisplayName = typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetCustomAttribute<MetaPartsDescriptor>()?.DisplayName,
  428. Description =
  429. typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetCustomAttribute<MetaPartsDescriptor>()?.Description,
  430. ParametersJsonSchemaStrings =
  431. typeof(HalfDuplexActivePollingDeviceProcessor<,>).GetConstructors().WithAttributeOrAll<ParamsJsonSchemas>().Select(ctor=>ctor.ResolveParamsJsonSchemas()).ToList(),
  432. }
  433. }
  434. };
  435. groupDescriptors.Add(processorGroupDescriptor);
  436. }
  437. pmcDescriptor.MetaPartsGroupDescriptors = groupDescriptors;
  438. }
  439. descriptors.Add(pmcDescriptor);
  440. }
  441. return descriptors;
  442. }
  443. /// <summary>
  444. /// call the fallback config resolve method from target metaparts type.
  445. /// </summary>
  446. /// <param name="metaPartsConfig"></param>
  447. /// <returns>return true for called the method and updated the ParametersJsonArrayStr successfully,
  448. /// false indicates no fallback config resolve method defined, and no update.</returns>
  449. public static bool TryCallMetaPartsConfigCompatibilityMethodAndUpdate(this ProcessorMetaPartsConfig metaPartsConfig)
  450. {
  451. var metaPartsType = Type.GetType(metaPartsConfig.FullTypeString);// ObjectInstanceCreator.CurrentDomainProcessorEndPointTypes;
  452. if (metaPartsType == null) return false;// throw new ArgumentException("Could not find any metaPartsType that has type full name to: " + metaPartsConfig.FullTypeString);
  453. string methodName = "ResolveCtorMetaPartsConfigCompatibility";
  454. var resolveCompatibleMethodInfo =
  455. metaPartsType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
  456. .Where(mi => mi.Name == methodName
  457. && mi.ReturnType == typeof(List<object>)).FirstOrDefault();
  458. if (resolveCompatibleMethodInfo == null)
  459. {
  460. // no resolve method defined in metaParts type, unable to resolve.
  461. return false;
  462. }
  463. else
  464. {
  465. try
  466. {
  467. var reformatParams = resolveCompatibleMethodInfo.Invoke(null,
  468. new object[] { metaPartsConfig.ParametersJsonArrayStr }
  469. ) as List<object>;
  470. var jsonSerializerOptions = new JsonSerializerOptions()
  471. {
  472. WriteIndented = true,
  473. PropertyNameCaseInsensitive = true,
  474. };
  475. jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
  476. var reformatParamsStr = "[" + reformatParams.Select(p => JsonSerializer.Serialize(p)).Aggregate((acc, n) => acc + ", " + n) + "]";
  477. metaPartsConfig.ParametersJsonArrayStr = reformatParamsStr;
  478. return true;
  479. }
  480. catch { return false; }
  481. }
  482. }
  483. public static List<string> ResolveParamsJsonSchemas(this MethodBase methodOrCtor)
  484. {
  485. Type declaringType = methodOrCtor.DeclaringType;
  486. var apiDesc = methodOrCtor.GetCustomAttribute<ParamsJsonSchemas>();
  487. if (apiDesc != null)
  488. {
  489. if (!string.IsNullOrEmpty(apiDesc.SchemaEmbededResourceName))
  490. {
  491. var resourceName = declaringType.Assembly.GetManifestResourceNames().FirstOrDefault(n => n.EndsWith("." + apiDesc.SchemaEmbededResourceName));
  492. if (resourceName != null)
  493. {
  494. var resourceStream = declaringType.Assembly.GetManifestResourceStream(resourceName);
  495. if (resourceStream != null)
  496. using (var sr = new StreamReader(resourceStream))
  497. {
  498. var content = sr.ReadToEnd();
  499. try
  500. {
  501. var jsonSchemaRootElement = JsonDocument.Parse(content).RootElement;
  502. if (jsonSchemaRootElement.ValueKind == JsonValueKind.Array)
  503. return jsonSchemaRootElement.EnumerateArray().Select(jse => jse.GetRawText()).ToList();
  504. return new List<string>() { content };
  505. }
  506. catch (Exception exxx) { }
  507. }
  508. }
  509. }
  510. if (apiDesc.SchemaStrings != null && apiDesc.SchemaStrings.Any()
  511. && apiDesc.SchemaStrings.Length ==
  512. methodOrCtor.GetParameters().Where(p =>
  513. !p.ParameterType.IsInterface && !p.ParameterType.IsAbstract).Count())
  514. return apiDesc.SchemaStrings.ToList();
  515. }
  516. var generator = new JSchemaGenerator();
  517. generator.GenerationProviders.Add(new StringEnumGenerationProvider());
  518. return methodOrCtor.GetParameters().Where(p => !p.ParameterType.IsInterface && !p.ParameterType.IsAbstract)
  519. .Select(p =>
  520. {
  521. int serializeDepth = 0;
  522. if (IsTypeDepthTooHighForSerialize(p.ParameterType, ref serializeDepth))
  523. return "{}".Insert(1, "\"title\": \"" + p.Name + " ----> of type: " + p.ParameterType.Name + " 's Depth is Too High for Serialize and Generate Json Sample, try avoid recursive properties\", \"type\": \"object\"");
  524. else
  525. return generator.Generate(p.ParameterType).ToString().Insert(1, "\"title\": \"" + p.Name + "\",");
  526. }).ToList();
  527. }
  528. /// <summary>
  529. /// Test the target type for if its serialize depth excceed 64.
  530. /// </summary>
  531. /// <param name="targetType"></param>
  532. /// <param name="depth">must set to 0 when call the method</param>
  533. /// <returns>true indicates this type is invalid for json serialize.</returns>
  534. private static bool IsTypeDepthTooHighForSerialize(Type targetType, ref int depth)
  535. {
  536. var targetProperties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.PropertyType.IsClass && !p.PropertyType.IsPrimitive);
  537. foreach (var p in targetProperties)
  538. {
  539. depth++;
  540. if (depth >= 64) return true;
  541. if (IsTypeDepthTooHighForSerialize(p.PropertyType, ref depth))
  542. return true;
  543. else
  544. continue;
  545. }
  546. return false;
  547. }
  548. public static string ResolveParamsJsonSchemas(this PropertyInfo propertyInfo)
  549. {
  550. Type declaringType = propertyInfo.PropertyType;//.DeclaringType;
  551. var generator = new JSchemaGenerator();
  552. generator.GenerationProviders.Add(new StringEnumGenerationProvider());
  553. return generator.Generate(declaringType).ToString();//.Insert(1, "\"title\": \"" + propertyInfo.Name + "\",");
  554. }
  555. public static string ResolveParamsJsonSchema(this Type type)
  556. {
  557. var generator = new JSchemaGenerator();
  558. generator.GenerationProviders.Add(new StringEnumGenerationProvider());
  559. return generator.Generate(type).ToString();
  560. }
  561. /// <summary>
  562. /// Generate a random Json sample based on json schema
  563. /// </summary>
  564. /// <param name="schema"></param>
  565. /// <returns>a random Json sample</returns>
  566. public static JToken GenerateJsonSample(this JSchema schema)
  567. {
  568. JToken output;
  569. switch (schema.Type)
  570. {
  571. case JSchemaType.Object:
  572. var jObject = new JObject();
  573. if (schema.Properties != null)
  574. {
  575. foreach (var prop in schema.Properties)
  576. {
  577. jObject.Add(LowerCaseFirstChar(prop.Key), GenerateJsonSample(prop.Value));
  578. }
  579. }
  580. output = jObject;
  581. break;
  582. case JSchemaType.Object | JSchemaType.Null:
  583. var jObject2 = new JObject();
  584. if (schema.Properties != null)
  585. {
  586. foreach (var prop in schema.Properties)
  587. {
  588. jObject2.Add(LowerCaseFirstChar(prop.Key), GenerateJsonSample(prop.Value));
  589. }
  590. }
  591. output = jObject2;
  592. break;
  593. case JSchemaType.Array:
  594. var jArray = new JArray();
  595. foreach (var item in schema.Items)
  596. {
  597. jArray.Add(GenerateJsonSample(item));
  598. }
  599. output = jArray;
  600. break;
  601. case JSchemaType.Array | JSchemaType.Null:
  602. var jArray2 = new JArray();
  603. foreach (var item in schema.Items)
  604. {
  605. jArray2.Add(GenerateJsonSample(item));
  606. }
  607. output = jArray2;
  608. break;
  609. case JSchemaType.String:
  610. if (schema.Format == "date-time")
  611. output = new JValue("2020-04-01T00:01:02.8514872+08:00");
  612. else if (schema.Enum != null && schema.Enum.Any())
  613. {
  614. if (string.IsNullOrEmpty(schema.Enum.First().ToString()))
  615. {
  616. if (schema.Enum.Count >= 2)
  617. output = new JValue(schema.Enum[1].ToString());
  618. else
  619. output = Newtonsoft.Json.Linq.JValue.CreateNull();
  620. }
  621. else
  622. output = new JValue(schema.Enum.First().ToString());
  623. }
  624. else
  625. output = new JValue("string_sample");
  626. break;
  627. case JSchemaType.String | JSchemaType.Null:
  628. if (schema.Format == "date-time")
  629. output = new JValue("2020-04-01T00:01:02.8514872+08:00");
  630. else if (schema.Enum != null && schema.Enum.Any())
  631. {
  632. if (string.IsNullOrEmpty(schema.Enum.First().ToString()))
  633. {
  634. if (schema.Enum.Count >= 2)
  635. output = new JValue(schema.Enum[1].ToString());
  636. else
  637. output = JValue.CreateNull();
  638. }
  639. else
  640. output = new JValue(schema.Enum.First().ToString());
  641. }
  642. else
  643. output = new JValue("nullable_string_sample");
  644. break;
  645. case JSchemaType.Number:
  646. output = new JValue(1.0);
  647. break;
  648. case JSchemaType.Integer:
  649. output = new JValue(1);
  650. break;
  651. case JSchemaType.Boolean:
  652. output = new JValue(false);
  653. break;
  654. case JSchemaType.Null:
  655. output = JValue.CreateNull();
  656. break;
  657. // it's a type of object
  658. case Newtonsoft.Json.Schema.JSchemaType.String | Newtonsoft.Json.Schema.JSchemaType.Number | Newtonsoft.Json.Schema.JSchemaType.Integer | Newtonsoft.Json.Schema.JSchemaType.Boolean | Newtonsoft.Json.Schema.JSchemaType.Object | Newtonsoft.Json.Schema.JSchemaType.Array:
  659. output = JValue.CreateNull();
  660. break;
  661. default:
  662. output = null;
  663. break;
  664. }
  665. return output;
  666. }
  667. private static string LowerCaseFirstChar(string name)
  668. {
  669. return name.Substring(0, 1).ToLower() + name.Substring(1);
  670. }
  671. public static string GenerateParametersJsonSample(this string[] parametersJsonSchemaStrings)
  672. {
  673. if (parametersJsonSchemaStrings == null || !parametersJsonSchemaStrings.Any()) return "[]";
  674. var jTokens = parametersJsonSchemaStrings.Select(s => JSchema.Parse(s)).Select(js => js.GenerateJsonSample());
  675. return "[" + jTokens.Select(jt => jt.Type == JTokenType.String ? "\"" + jt.ToString() + "\"" : jt.ToString()).Aggregate((acc, n) => acc + ", " + n) + "]";
  676. }
  677. public static string GenerateParameterJsonSample(this string parameterJsonSchemaString)
  678. {
  679. if (string.IsNullOrEmpty(parameterJsonSchemaString)) return "";
  680. var jToken = JSchema.Parse(parameterJsonSchemaString).GenerateJsonSample();
  681. return jToken.ToString();
  682. }
  683. }
  684. }