ExtensionMethods.cs 39 KB

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