LogicHandler.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. using Logs;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using WCS.Entity;
  9. using WCS.Service;
  10. namespace WCS.Core
  11. {
  12. /// <summary>
  13. /// 逻辑处理器
  14. /// </summary>
  15. public abstract class LogicHandler
  16. {
  17. /// <summary>
  18. /// 停止
  19. /// </summary>
  20. protected bool Stoped = false;
  21. /// <summary>
  22. /// 描述
  23. /// </summary>
  24. protected string Description { get; }
  25. /// <summary>
  26. /// 所有对象
  27. /// </summary>
  28. public static List<object> AllObjects = new();
  29. /// <summary>
  30. /// 所有处理器
  31. /// </summary>
  32. private static readonly List<LogicHandler> Handlers = new();
  33. /// <summary>
  34. /// 日志委托,暂时弃用
  35. /// </summary>
  36. [Obsolete]
  37. public static Action<string, string, string> DbLog;
  38. /// <summary>
  39. /// 逻辑处理器
  40. /// </summary>
  41. protected LogicHandler()
  42. {
  43. var attr = this.GetType().GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
  44. Description = attr != null ? attr.Description : this.GetType().Name;
  45. }
  46. /// <summary>
  47. /// 开始
  48. /// </summary>
  49. public abstract void Start();
  50. /// <summary>
  51. /// 是否并行
  52. /// </summary>
  53. public virtual bool ParallelRun => false;
  54. /// <summary>
  55. /// 数据刷新时触发
  56. /// </summary>
  57. /// <param name="milliseconds">离上次触发的间隔时间(毫秒)</param>
  58. public abstract void Update(double milliseconds);
  59. /// <summary>
  60. /// 停止
  61. /// </summary>
  62. public void Stop()
  63. {
  64. Stoped = true;
  65. }
  66. /// <summary>
  67. /// 添加处理器
  68. /// </summary>
  69. /// <param name="handler">处理器</param>
  70. public static void AddManager(LogicHandler handler)
  71. {
  72. Handlers.Add(handler);
  73. }
  74. /// <summary>
  75. /// 启动所有的处理器
  76. /// </summary>
  77. public static void StartAll()
  78. {
  79. foreach (var h in Handlers)
  80. {
  81. h.Start();
  82. }
  83. var th = new Thread(Loop)
  84. {
  85. IsBackground = true //前台线程即主线程关闭,立即结束该线程
  86. };
  87. th.Start();
  88. }
  89. /// <summary>
  90. /// 最后一次执行结束的时间
  91. /// </summary>
  92. private static DateTime _last = DateTime.Now;
  93. /// <summary>
  94. ///逻辑耗时
  95. /// </summary>
  96. private static int _logicTimes;
  97. public static DateTime Frame { get; private set; }
  98. /// <summary>
  99. /// 主循环体,用于PLC数据的读取与逻辑执行
  100. /// </summary>
  101. private static void Loop()
  102. {
  103. var arr = AllObjects.OfType<WCS_DEVICE>().Where(v => v.ENABLED).SelectMany(v => v.PROTOCOLS)
  104. .Where(v => v.ENABLED && v.DB.ENABLED && v.DB.PLC.ENABLED)
  105. .GroupBy(v => v.DB).Select(v => v.Key)
  106. .ToArray();
  107. while (true)
  108. {
  109. var dd = DateTime.Now;
  110. Frame = DateTime.Now;
  111. Parallel.ForEach(arr, db =>
  112. {
  113. try
  114. {
  115. Ltc.SetChannel("刷新");
  116. db.Ex().DataRefresh();
  117. }
  118. catch (Exception ex)
  119. {
  120. Console.WriteLine("更新" + db.NAME + "数据失败:" + ex.Message);
  121. }
  122. });
  123. var dbTimes = (DateTime.Now - dd).TotalMilliseconds;
  124. var total = (DateTime.Now - _last).TotalMilliseconds;
  125. var s = (int)(600 - total);
  126. if (s > 0)
  127. Thread.Sleep(s);
  128. total = (DateTime.Now - _last).TotalMilliseconds;
  129. Console.ForegroundColor = ConsoleColor.Blue;
  130. //此处打印的logicTimes实际是上一个周期的业务处理时长
  131. Console.WriteLine("------刷新DB块数据耗时:" + ((int)dbTimes).ToString().PadRight(4, ' ') + ";业务逻辑耗时:" + ((int)_logicTimes).ToString().PadRight(4, ' ') + ";周期总耗时" + ((int)total).ToString().PadRight(4, ' ') + "");
  132. Console.ResetColor();
  133. _last = DateTime.Now;
  134. Parallel.ForEach(Handlers, m =>
  135. {
  136. var dm = DateTime.Now;
  137. try
  138. {
  139. m.Update(total);
  140. }
  141. catch (Exception)
  142. {
  143. //TODO:增加一个异常记录
  144. }
  145. var dm2 = (DateTime.Now - dm).TotalMilliseconds;
  146. });
  147. Configs.Publish();
  148. _logicTimes = (int)(DateTime.Now - _last).TotalMilliseconds;
  149. }
  150. }
  151. /// <summary>
  152. /// 停止所有的处理器
  153. /// </summary>
  154. public static void StopAll()
  155. {
  156. foreach (var h in Handlers)
  157. {
  158. try
  159. {
  160. h.Stop();
  161. }
  162. catch
  163. {
  164. // TODO:等待处理
  165. }
  166. }
  167. }
  168. }
  169. /// <summary>
  170. /// 逻辑处理器 泛型
  171. /// </summary>
  172. /// <typeparam name="T"></typeparam>
  173. public abstract class LogicHandler<T> : LogicHandler where T : EntityEx<WCS_DEVICE>
  174. {
  175. /// <summary>
  176. /// 业务类集合
  177. /// </summary>
  178. protected List<WorkInfo<T>> Works = new();
  179. /// <summary>
  180. /// TODO:?
  181. /// </summary>
  182. private IEnumerable<T> _objects = null;
  183. protected IEnumerable<T> Objects
  184. {
  185. get
  186. {
  187. _objects ??= AllObjects.OfType<WCS_DEVICE>().Where(SelectDevice)
  188. .Select(v => Activator.CreateInstance(typeof(T), v)).OfType<T>().ToArray();
  189. return _objects.Where(v => v.Entity.ENABLED && v.Entity.PROTOCOLS.All(d => d.ENABLED && d.DB.ENABLED && d.DB.PLC.ENABLED));
  190. }
  191. }
  192. /// <summary>
  193. /// 写入设备信息
  194. /// </summary>
  195. /// <param name="dev">设备</param>
  196. /// <returns></returns>
  197. private static bool SelectDevice(WCS_DEVICE dev)
  198. {
  199. var typenames = typeof(T).GenericTypeArguments.Select(v => v.AssemblyQualifiedName).ToArray();
  200. var res = typenames.All(v => dev.PROTOCOLS.Any(d => d.DB.PROTOCOL == v));
  201. return res;
  202. }
  203. /// <summary>
  204. /// 添加Work
  205. /// </summary>
  206. /// <param name="condition"></param>
  207. /// <param name="work">work</param>
  208. /// <param name="parallel">是否并发</param>
  209. public void AddWork(Func<T, bool> condition, Action<T> work, bool parallel = false)
  210. {
  211. var title = work.Method.Name;
  212. var attr = work.Method.GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
  213. if (attr != null)
  214. title = attr.Description;
  215. var arr = Objects.Where(condition).ToArray();
  216. this.Works.Add(new WorkInfo<T> { Work = work, Params = arr, Title = title, Parallel = parallel });
  217. }
  218. /// <summary>
  219. ///
  220. /// </summary>
  221. /// <param name="arr"></param>
  222. /// <param name="work"></param>
  223. /// <param name="parallel"></param>
  224. public void AddWork(IEnumerable<T> arr, Action<T> work, bool parallel = false)
  225. {
  226. var title = work.Method.Name;
  227. var attr = work.Method.GetCustomAttributes(false).OfType<DescriptionAttribute>().FirstOrDefault();
  228. if (attr != null)
  229. title = attr.Description;
  230. this.Works.Add(new WorkInfo<T> { Work = work, Params = arr, Title = title, Parallel = parallel });
  231. }
  232. /// <summary>
  233. /// 开始执行业务流程
  234. /// </summary>
  235. /// <param name="milliseconds"></param>
  236. public override void Update(double milliseconds)
  237. {
  238. if (ParallelRun)
  239. {
  240. Parallel.ForEach(Works, DoWork);
  241. }
  242. else
  243. {
  244. foreach (var w in Works)
  245. {
  246. DoWork(w);
  247. }
  248. }
  249. }
  250. /// <summary>
  251. /// 执行Work
  252. /// </summary>
  253. /// <param name="work"></param>
  254. protected virtual void DoWork(WorkInfo<T> work)
  255. {
  256. if (work.Parallel)
  257. {
  258. Parallel.ForEach(work.Params, p =>
  259. {
  260. Do(work, p);
  261. });
  262. }
  263. else
  264. {
  265. foreach (var p in work.Params)
  266. {
  267. Do(work, p);
  268. }
  269. }
  270. }
  271. /// <summary>
  272. /// 开始执行
  273. /// </summary>
  274. /// <param name="wi"></param>
  275. /// <param name="p"></param>
  276. protected virtual void Do(WorkInfo<T> wi, T p)
  277. {
  278. var channel = Description + "." + wi.Title + "." + p;
  279. try
  280. {
  281. Ltc.SetChannel(channel);
  282. Ltc.Log("开始---------------------------------------");
  283. wi.Work(p);
  284. }
  285. catch (DoException ex)
  286. {
  287. InfoLog.INFO_INFO($"[{channel}]--{ex.Message}");
  288. }
  289. //WarnException进阶条件未满足,添加数据库,记录文本日志、数据库,上抛WCS,上抛WMS
  290. catch (WarnException ex)
  291. {
  292. InfoLog.INFO_WARN($"[{channel}]--{ex.Message}");
  293. Ltc.Log(ex.GetBaseException().Message);
  294. Configs.UploadException?.Invoke(p.ToString(), ex.GetBaseException().Message);
  295. }
  296. //未知异常,仅记录文本日志,需定期排查该文件,检查系统是否有未知异常,并处理
  297. catch (Exception ex)
  298. {
  299. InfoLog.INFO_ERROR($"[{channel}]--{ex.Message}--{ex.StackTrace}");
  300. }
  301. finally
  302. {
  303. Ltc.Log("结束\n");
  304. }
  305. }
  306. /// <summary>
  307. /// 此方法不会被自动调用,请在Start方法中使用AddWork将其添加至工作队列
  308. /// </summary>
  309. /// <param name="dev"></param>
  310. [Description("执行")]
  311. protected abstract void Execute(T dev);
  312. }
  313. public class WorkTitleAttribute : Attribute
  314. {
  315. public Type Handler { get; set; }
  316. public string Title { get; set; }
  317. public bool Parallel { get; set; }
  318. public WorkTitleAttribute(Type handler, string title, bool parallel = false)
  319. {
  320. this.Handler = handler;
  321. this.Title = title;
  322. this.Parallel = parallel;
  323. }
  324. }
  325. public abstract class Work
  326. {
  327. public abstract IEnumerable<object> GetObjs();
  328. public abstract void Execute(object obj);
  329. }
  330. public abstract class Work<T> : Work
  331. {
  332. public override sealed void Execute(object obj)
  333. {
  334. Do((T)obj);
  335. }
  336. public override sealed IEnumerable<object> GetObjs()
  337. {
  338. return InitObjects().OfType<object>().ToArray();
  339. }
  340. protected abstract void Do(T obj);
  341. protected abstract bool SelectDevice(WCS_DEVICE dev);
  342. protected virtual IEnumerable<T> InitObjects()
  343. {
  344. var arr = Device.Where(v => v.ENABLED)
  345. .Where(SelectDevice).ToArray();
  346. var res = arr.Select(v => (T)Activator.CreateInstance(typeof(T), v));
  347. return res;
  348. }
  349. }
  350. public abstract class DeviceWork<T> : Work<T> where T : EntityEx<WCS_DEVICE>
  351. {
  352. private readonly string[] _typenames;
  353. protected DeviceWork()
  354. {
  355. _typenames = typeof(T).GenericTypeArguments.Select(v => v.AssemblyQualifiedName).ToArray();
  356. }
  357. protected abstract override void Do(T obj);
  358. protected override sealed IEnumerable<T> InitObjects()
  359. {
  360. var arr = Device.Where(v => v.ENABLED && v.PROTOCOLS.All(d => d.ENABLED && d.DB.ENABLED && d.DB.PLC.ENABLED))
  361. .Where(v => _typenames.All(d => v.PROTOCOLS.Any(e => e.DB.PROTOCOL == d)))
  362. .Where(SelectDevice).ToArray();
  363. var res = arr.Select(v => Activator.CreateInstance(typeof(T), v) as T);
  364. return res;
  365. }
  366. }
  367. }