QueuingThread.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using parkMonitor.entity;
  8. using parkMonitor.model;
  9. using parkMonitor.server;
  10. using System.Configuration;
  11. using parkMonitor.tools;
  12. using parkMonitor.server.uiLogServer;
  13. namespace parkMonitor.server.CoreThread
  14. {
  15. /// <summary>
  16. /// 队列线程,处理号牌与停取车指令业务逻辑
  17. /// </summary>
  18. public class QueuingThread : IEquipments
  19. {
  20. Queue<NumberMachineNode> LicenseQueue = new Queue<NumberMachineNode>();
  21. Queue<Command> StoreCmdQueue = new Queue<Command>();
  22. Queue<Command> ValidStoreCmdQueue = new Queue<Command>();
  23. Queue<Command> FetchCmdQueue = new Queue<Command>();
  24. BlockingQueue ExecutableCmdQueue = new BlockingQueue();
  25. int fetchStoreRatio, licenseTime, userTime;
  26. bool isClosing = false;
  27. IEquipments webHandle = null;
  28. IEquipments numMachineHandle = null;
  29. NumberMachineNode license;
  30. MessageUTF8 webMsg;
  31. /// <summary>
  32. /// 队列构造函数
  33. /// </summary>
  34. public QueuingThread()
  35. {
  36. webHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.Web);
  37. numMachineHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.NumMachine);
  38. try
  39. {
  40. fetchStoreRatio = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_store_ratio"));
  41. licenseTime = Int32.Parse(ConfigurationManager.AppSettings.Get("licenseTime"));
  42. userTime = Int32.Parse(ConfigurationManager.AppSettings.Get("userTime"));
  43. }
  44. catch (Exception) { fetchStoreRatio = 3; UILogServer.ins.error("未找到停取车比例与计时参数"); }
  45. }
  46. /// <summary>
  47. /// 从web线程获得用户指令
  48. /// </summary>
  49. private void ReceiveFromWebThread()
  50. {
  51. while (!isClosing)
  52. {
  53. Command cmd = null;
  54. //接收web消息
  55. if (webHandle != null)
  56. {
  57. try
  58. {
  59. webMsg = (MessageUTF8)((MessageUTF8)webHandle.GetMessage()).Clone();
  60. }
  61. catch (Exception) { UILogServer.ins.error("用户指令异常"); }
  62. }
  63. //get useful data from webMsg and assign it to output data structure
  64. if (webMsg != null)
  65. {
  66. cmd = new Command();
  67. cmd.commandType = webMsg.cmd;
  68. cmd.LicenseNum = webMsg.context;
  69. cmd.userID = webMsg.sender;
  70. cmd.garageID = webMsg.garageID;
  71. cmd.parkingRecordsID = webMsg.parkingRecordsID;
  72. cmd.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  73. webMsg = null;
  74. }
  75. //input user command
  76. if (cmd != null)
  77. {
  78. if (cmd.commandType.Equals('f'))
  79. {
  80. lock (FetchCmdQueue)
  81. {
  82. FetchCmdQueue.Enqueue((Command)cmd.Clone());
  83. }
  84. UILogServer.ins.info("收到web取车指令:" + cmd.LicenseNum);
  85. }
  86. //停车先遍历
  87. else if (cmd.commandType.Equals('s'))
  88. {
  89. UserCmdMatch(cmd);
  90. UILogServer.ins.info("收到web停车指令:" + cmd.LicenseNum);
  91. }
  92. else
  93. {
  94. Console.WriteLine("invalid user command.");
  95. UILogServer.ins.error("从web收到无法识别的用户指令,并非停取车");
  96. }
  97. }
  98. Thread.Sleep(500);
  99. }
  100. }
  101. /// <summary>
  102. /// 队列线程主业务逻辑
  103. /// </summary>
  104. private void Run()
  105. {
  106. NumberMachineNode temp = null;
  107. while (!isClosing)
  108. {
  109. lock (LicenseQueue)
  110. {
  111. //本地号牌已使用,则接收新的号牌消息
  112. if (numMachineHandle != null && !NumMachineNodeValidation(license))
  113. {
  114. license = ((NumberMachineMessage)numMachineHandle.GetMessage()).aNode;
  115. //无重复则匹配指令,匹配失败则入号牌队列,并将号牌机线程获得的Node中ip标记为已使用
  116. if (NumMachineNodeValidation(license) && !LicenseQueue.Contains(license))
  117. {
  118. UILogServer.ins.info("已扫描到号牌:" + license.LicenseNum + ",匹配中");
  119. Console.WriteLine("已扫描到号牌:" + license.LicenseNum + ",匹配中");
  120. //先匹配指令
  121. NumMatch(ref license);
  122. }
  123. }
  124. //将已被使用的号牌从号牌队列排除
  125. for (int i = 0; i < LicenseQueue.Count; i++)
  126. {
  127. temp = LicenseQueue.Dequeue();
  128. if (temp != null && temp.ip != "used")
  129. {
  130. LicenseQueue.Enqueue(temp);
  131. }
  132. }
  133. }
  134. //队列业务逻辑
  135. //UserCmdMatch();
  136. Scheduling();
  137. lock (StoreCmdQueue)
  138. {
  139. UserCmdTimeOut();
  140. }
  141. lock (LicenseQueue)
  142. {
  143. NumTimeOut();
  144. }
  145. Thread.Sleep(500);
  146. }
  147. }
  148. /// <summary>
  149. /// 号牌节点有效性验证
  150. /// </summary>
  151. /// <param name="node"></param>
  152. /// <returns></returns>
  153. private bool NumMachineNodeValidation(NumberMachineNode node)
  154. {
  155. return (node != null && node.ip != null && node.ip != "" && node.ip != "used") ? true : false;
  156. }
  157. /// <summary>
  158. /// 停车指令超时处理
  159. /// </summary>
  160. private void UserCmdTimeOut()
  161. {
  162. Command cmd = null;
  163. for (int i = 0; i < StoreCmdQueue.Count; i++)
  164. {
  165. cmd = StoreCmdQueue.Dequeue();
  166. //指令超时处理
  167. TimeSpan userTimeSpan = DateTime.Now - DateTime.Parse(cmd.TimeRecord);
  168. //Console.WriteLine("指令已扫描时间:" + userTimeSpan.TotalSeconds);
  169. if (userTimeSpan.TotalMinutes >= userTime)
  170. {
  171. UILogServer.ins.error("异常:用户指令 "+ cmd.LicenseNum+ " 已存在超过" + userTimeSpan.TotalMinutes + "分钟 ,强制出队");
  172. //cmd.LicenseNum = "";
  173. cmd.commandType = 'e';
  174. lock (ValidStoreCmdQueue)
  175. {
  176. ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
  177. }
  178. if (StoreCmdQueue.Count != 0)
  179. cmd = StoreCmdQueue.Dequeue();
  180. else
  181. break;
  182. }
  183. //未超时则归队
  184. else
  185. {
  186. StoreCmdQueue.Enqueue((Command)cmd.Clone());
  187. }
  188. }
  189. }
  190. /// <summary>
  191. /// 号牌超时处理
  192. /// </summary>
  193. private void NumTimeOut()
  194. {
  195. for (int i = 0; i < LicenseQueue.Count; i++)
  196. {
  197. NumberMachineNode node = LicenseQueue.Dequeue();
  198. if (NumMachineNodeValidation(node))
  199. {
  200. //计算时间差,超过5分钟自动出队(?处理方式待讨论),并将该Node标记为已使用,等待统一处理
  201. TimeSpan licTimeSpan = DateTime.Now - DateTime.Parse(node.TimeRecord);
  202. if (licTimeSpan.TotalMinutes >= licenseTime)
  203. {
  204. //异常号牌不再入队,而是界面提示管理员,并让号牌机线程
  205. UILogServer.ins.error("异常:号牌 " + node.LicenseNum + " 已扫描超过" + licTimeSpan.TotalMinutes + "分钟 ,强制出队");
  206. //停车完成,将相应车牌复位
  207. NumberMachineMessage numberMachineMessage = new NumberMachineMessage();
  208. numberMachineMessage.aNode = new NumberMachineNode();
  209. numberMachineMessage.aNode.ip = "";
  210. numberMachineMessage.aNode.LicenseNum = node.LicenseNum;
  211. numMachineHandle.SetMessage(numberMachineMessage);
  212. node.ip = "used";
  213. }
  214. }
  215. LicenseQueue.Enqueue(node);
  216. }
  217. }
  218. /// <summary>
  219. /// 单用户指令匹配号牌队列
  220. /// </summary>
  221. private void UserCmdMatch(Command cmd)
  222. {
  223. lock (StoreCmdQueue)
  224. {
  225. NumberMachineNode num = null;
  226. bool matched = false;
  227. num = new NumberMachineNode();
  228. lock (LicenseQueue)
  229. {
  230. for (int i = 0; i < LicenseQueue.Count; i++)
  231. {
  232. num = LicenseQueue.Dequeue();
  233. //匹配成功
  234. if (NumMachineNodeValidation(num) && cmd.LicenseNum.Equals(num.LicenseNum))
  235. {
  236. cmd.TimeRecord = num.TimeRecord;
  237. cmd.ip = num.ip;
  238. cmd.id = num.id;
  239. ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
  240. matched = true;
  241. break;
  242. }
  243. else
  244. {
  245. LicenseQueue.Enqueue((NumberMachineNode)num.Clone());
  246. }
  247. }
  248. }
  249. if (!matched)
  250. {
  251. StoreCmdQueue.Enqueue((Command)cmd.Clone());
  252. }
  253. }
  254. }
  255. /// <summary>
  256. /// 单号牌匹配用户指令队列
  257. /// </summary>
  258. private void NumMatch(ref NumberMachineNode license)
  259. {
  260. Command cmd = new Command();
  261. bool matched = false;
  262. lock (StoreCmdQueue)
  263. {
  264. for (int i = 0; NumMachineNodeValidation(license) && i < StoreCmdQueue.Count; i++)
  265. {
  266. cmd = StoreCmdQueue.Dequeue();
  267. //匹配成功
  268. if (license.LicenseNum.Equals(cmd.LicenseNum))
  269. {
  270. cmd.TimeRecord = license.TimeRecord;
  271. cmd.ip = license.ip;
  272. cmd.id = license.id;
  273. ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
  274. matched = true;
  275. break;
  276. }
  277. else
  278. {
  279. StoreCmdQueue.Enqueue((Command)cmd.Clone());
  280. }
  281. }
  282. }
  283. lock (LicenseQueue)
  284. {
  285. if (!matched)
  286. {
  287. LicenseQueue.Enqueue((NumberMachineNode)license.Clone());
  288. }
  289. }
  290. license.ip = "used";
  291. }
  292. /// <summary>
  293. /// 停取车调度,控制停取车比例,可多取一停,也可一取一停
  294. /// </summary>
  295. private void Scheduling()
  296. {
  297. //scheduling algorithm
  298. int fetch = fetchStoreRatio;
  299. Command cmd = null;
  300. lock (FetchCmdQueue)
  301. {
  302. while (FetchCmdQueue.Count != 0 && fetch-- > 0)
  303. {
  304. cmd = FetchCmdQueue.Dequeue();
  305. ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
  306. UILogServer.ins.info("任务:取出号牌为 " + cmd.LicenseNum + " 的车辆,开始排队");
  307. }
  308. }
  309. lock (ValidStoreCmdQueue)
  310. {
  311. if (ValidStoreCmdQueue.Count != 0)
  312. {
  313. cmd = ValidStoreCmdQueue.Dequeue();
  314. ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
  315. UILogServer.ins.info("任务:存入号牌为 " + cmd.LicenseNum + " 的车辆,开始排队");
  316. }
  317. }
  318. }
  319. /// <summary>
  320. /// 获取一个停取车指令
  321. /// </summary>
  322. public AbstractMessage GetMessage()
  323. {
  324. lock (ExecutableCmdQueue)
  325. {
  326. return (Command)ExecutableCmdQueue.Dequeue();
  327. }
  328. }
  329. /// <summary>
  330. /// 功能一:由核心线程调用,传入已存车完成需要标记的号牌
  331. /// 功能二:添加手动停取车指令
  332. /// </summary>
  333. public void SetMessage(AbstractMessage message)
  334. {
  335. //号牌机重置消息
  336. if (message.GetType().Equals(typeof(NumberMachineMessage)))
  337. {
  338. if (numMachineHandle != null)
  339. {
  340. numMachineHandle.SetMessage(message);
  341. }
  342. }
  343. //手动停取车指令
  344. else if (message.GetType().Equals(typeof(Command)))
  345. {
  346. Command command = (Command)message;
  347. //command.garageID = 0;
  348. //command.parkingRecordsID = 0;
  349. command.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  350. //input user command
  351. if (command != null)
  352. {
  353. if (command.returnedCount > 0)
  354. {
  355. if (command.commandType.Equals('s'))
  356. {
  357. lock (ValidStoreCmdQueue)
  358. {
  359. ValidStoreCmdQueue.Enqueue((Command)command.Clone());
  360. }
  361. UILogServer.ins.info("收到退回的停车指令:" + command.LicenseNum);
  362. }
  363. else if(command.commandType.Equals('f')){
  364. lock (FetchCmdQueue)
  365. {
  366. FetchCmdQueue.Enqueue((Command)command.Clone());
  367. }
  368. UILogServer.ins.info("收到退回的取车指令:" + command.LicenseNum);
  369. }
  370. }
  371. else
  372. {
  373. if (command.commandType.Equals('f'))
  374. {
  375. lock (FetchCmdQueue)
  376. {
  377. FetchCmdQueue.Enqueue((Command)command.Clone());
  378. }
  379. UILogServer.ins.info("收到手动的取车指令:" + command.LicenseNum);
  380. }
  381. else if (command.commandType.Equals('s'))
  382. {
  383. UserCmdMatch(command);
  384. UILogServer.ins.info("收到手动的停车指令:" + command.LicenseNum);
  385. }
  386. else
  387. {
  388. Console.WriteLine("invalid user command.");
  389. UILogServer.ins.error("收到无法识别的手动用户指令");
  390. }
  391. }
  392. }
  393. }
  394. }
  395. public void Start()
  396. {
  397. license = null;
  398. webMsg = null;
  399. Task.Factory.StartNew(() =>
  400. {
  401. ReceiveFromWebThread();
  402. });
  403. Task.Factory.StartNew(() =>
  404. {
  405. Run();
  406. });
  407. }
  408. public void Stop()
  409. {
  410. isClosing = true;
  411. }
  412. }
  413. }