LogicHandler.cs 13 KB

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