MainThreadClass.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. using CCDCount.DLL.Tools;
  2. using CCDCount.MODEL.CameraClass;
  3. using CCDCount.MODEL.ConfigModel;
  4. using CCDCount.MODEL.ShuLiClass;
  5. using LogClass;
  6. using MvCameraControl;
  7. using System;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  11. using System.Drawing;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Runtime.InteropServices;
  15. using System.Runtime.Remoting.Messaging;
  16. using System.Threading;
  17. namespace CCDCount.DLL
  18. {
  19. public class MainThreadClass
  20. {
  21. #region 变量与实例
  22. Thread SwitchIdentifyImageThread = null;
  23. private bool IsSwitch = false;
  24. public ShuLiClass shuLiClass = null;
  25. CameraClass cameraClass = new CameraClass();
  26. public string ThisCameraDevice = string.Empty;
  27. public string ThisCameraSN = string.Empty;
  28. public string ThisCamerName = string.Empty;
  29. public int ThisCamerNo = -1;
  30. //留给主界面的回调函数
  31. public event EventHandler<ActiveObjectEventArgsClass> WorkerToFrom;
  32. public bool CameraStatic { get { return _CameraStatic; } }
  33. private bool _CameraStatic = false;
  34. // 返回交换线程状态
  35. public bool CameraRunStatic { get { return IsSwitch; } }
  36. public bool IsOpenLoadThread { get { return _IsOpenLoadThread; }}
  37. private bool _IsOpenLoadThread = false;
  38. public int HistoryActiveNum { get { return shuLiClass.GetHistoryActiveNum(); } }
  39. public int OkHistoryNum { get { return shuLiClass.GetOkHistoryNum(); } }
  40. public int NgHistoryNum { get { return shuLiClass.GetNgHistoryNum(); } }
  41. // 数粒信息发送线程
  42. Thread SendBottLogicMessageThread = null;
  43. bool IsSend = false;
  44. // Modbus客户端实例
  45. ModbusTcpClient modbusTcpClient = null;
  46. // 颗粒结果待发送队列
  47. ConcurrentQueue<ushort> SendQueue = new ConcurrentQueue<ushort>();
  48. /// <summary>
  49. /// 数粒状态计时器
  50. /// </summary>
  51. Stopwatch stopwatch = Stopwatch.StartNew();
  52. /// <summary>
  53. /// 数粒状态
  54. /// </summary>
  55. private bool _ShuLiState = true;
  56. public bool ShuLiState { get { return _ShuLiState; } }
  57. #endregion
  58. #region 公共方法
  59. public void SetModbusClient(ModbusTcpClient modbusTcpClient)
  60. {
  61. this.modbusTcpClient = modbusTcpClient;
  62. if (!this.modbusTcpClient.IsTcpClientConnected())
  63. {
  64. LOG.error("ModbusTcpClient Connect Failed!");
  65. }
  66. else
  67. {
  68. StartSendBottLogicMessageThread();
  69. LOG.log("ModbusTcpClient Connect Success!", 6);
  70. }
  71. }
  72. public MainThreadClass(ShuLiConfigClass configClass,CameraConfig CameraConfig)
  73. {
  74. // 数粒配置文件地址
  75. if (configClass!=null)
  76. {
  77. // 创建数粒对象(配置文件)
  78. shuLiClass = new ShuLiClass(configClass);
  79. }
  80. else
  81. {
  82. // 创建数粒对象(默认)
  83. shuLiClass = new ShuLiClass();
  84. }
  85. _IsOpenLoadThread = CameraConfig.IsOpenLoad;
  86. ThisCameraSN = CameraConfig.CameraSNNum;
  87. ThisCamerName = CameraConfig.CameraName;
  88. ThisCameraDevice = CameraConfig.DeviceName;
  89. ThisCamerNo = CameraConfig.CamerNo;
  90. }
  91. /// <summary>
  92. /// 开始主线程
  93. /// </summary>
  94. public bool StartMianThread(CameraConfig DeviceConfig)
  95. {
  96. bool result = false;
  97. // 相机列表
  98. List<CameraInfoClass> list = new List<CameraInfoClass>();
  99. // 获取相机列表
  100. cameraClass.GetCameraList(out list);
  101. if (list.Count == 0)
  102. {
  103. LOG.error(string.Format("{0}:没有相机", "MainThreadClass-StartMianThread"));
  104. // 如果没有相机,则退出
  105. return result;
  106. }
  107. // 加载相机
  108. // cameraClass.LoadCamereDevice(list.First().DeviceSN);
  109. if(!cameraClass.LoadCamereDevice(DeviceConfig))
  110. {
  111. LOG.error(string.Format("{0}:相机加载失败", "MainThreadClass-StartMianThread"));
  112. return result;
  113. }
  114. // 取图线程开启
  115. cameraClass.StartCamera();
  116. _CameraStatic = true;
  117. // 数据交换线程开启
  118. StartSwitchThread();
  119. // 为数粒算法的识别成功一粒回调函数添加方法
  120. shuLiClass.WorkCompleted += Worker_OneGrainCompleted;
  121. // 开启识别线程
  122. shuLiClass.StartIdentifyFuntion(cameraClass.GetCamereImageSize().Width);
  123. result = true;
  124. return result;
  125. }
  126. /// <summary>
  127. /// 开始主线程
  128. /// </summary>
  129. public bool StartMianThread()
  130. {
  131. bool result = false;
  132. // 相机列表
  133. List<CameraInfoClass> list = new List<CameraInfoClass>();
  134. // 获取相机列表
  135. cameraClass.GetCameraList(out list);
  136. if (list.Count == 0)
  137. {
  138. // 如果没有相机,则退出
  139. return result;
  140. }
  141. // 为数粒算法的识别成功一粒回调函数添加方法
  142. shuLiClass.WorkCompleted += Worker_OneGrainCompleted;
  143. // 加载相机
  144. if (!cameraClass.LoadCamereDevice(new CameraConfig() { CameraSNNum = list.First().DeviceSN}))
  145. return result;
  146. _CameraStatic = true;
  147. // 取图线程开启
  148. cameraClass.StartCamera();
  149. // 数据交换线程开启
  150. StartSwitchThread();
  151. // 开启识别线程
  152. shuLiClass.StartIdentifyFuntion(cameraClass.GetCamereImageSize().Width);
  153. result = true;
  154. return result;
  155. }
  156. /// <summary>
  157. /// 停止主线程
  158. /// </summary>
  159. public void StopMianThread()
  160. {
  161. shuLiClass.WorkCompleted -= Worker_OneGrainCompleted;
  162. // 相机取图线程关闭
  163. cameraClass.StopCamera();
  164. // 数据交换线程关闭
  165. StopSwitchThread();
  166. // 数粒识别线程关闭
  167. shuLiClass.StopIdentifyFuntion();
  168. StopSendBottLogicMessageThread();
  169. }
  170. public bool ReLoadCamera(string CameraSN)
  171. {
  172. bool result = false;
  173. result = cameraClass.ReLoadCameraDevice(CameraSN);
  174. if (result)
  175. {
  176. CameraConfig ReloadCaonfig = cameraClass.GetConfigValue();
  177. ThisCameraSN = ReloadCaonfig.CameraSNNum;
  178. ThisCamerName = ReloadCaonfig.CameraName;
  179. ThisCameraDevice = ReloadCaonfig.DeviceName;
  180. }
  181. return result;
  182. }
  183. public void DisposeCamera()
  184. {
  185. cameraClass.StopCamera();
  186. ThisCameraSN = string.Empty;
  187. ThisCamerName = string.Empty;
  188. ThisCameraDevice = string.Empty;
  189. }
  190. /// <summary>
  191. /// 获取显示用的图片数据
  192. /// </summary>
  193. /// <param name="ImageHeight"></param>
  194. /// <param name="Data"></param>
  195. public void GetShowImage(int ImageHeight,out Bitmap ImageData)
  196. {
  197. List<RowStartEndCol> RowsShowList = new List<RowStartEndCol>();
  198. ActiveObjectClass NewActive = shuLiClass.GetLastActive();
  199. if (NewActive == null)
  200. {
  201. LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage"));
  202. ImageData = null;
  203. return;
  204. }
  205. List<ActiveObjectClass> Data = shuLiClass.GetHistoryActive().Where(o => o.LastSeenLine > NewActive.LastSeenLine - ImageHeight).ToList();
  206. Data.ForEach(o => o.RowsData.ForEach(p => RowsShowList.Add(p)));
  207. Bitmap BitmapImage = new Bitmap(NewActive.ImageWidth, ImageHeight);
  208. Graphics g = Graphics.FromImage(BitmapImage);
  209. Pen redPen = new Pen(Color.Red, 1);
  210. List<RowStartEndCol> ShowList = RowsShowList.Where(o => o.RowsCol > NewActive.LastSeenLine - BitmapImage.Height).ToList();
  211. RowsShowList.Where(o => o.RowsCol < NewActive.LastSeenLine - BitmapImage.Height).ToList().ForEach(o => RowsShowList.Remove(o));
  212. RowsShowList.Clear();
  213. ShowList.ForEach(o => g.DrawLine(redPen, new Point(o.StartCol, (int)(NewActive.LastSeenLine - o.RowsCol)), new Point(o.EndCol, (int)(NewActive.LastSeenLine - o.RowsCol))));
  214. ShowList.Clear();
  215. ImageData = BitmapImage.Clone() as Bitmap;
  216. BitmapImage.Dispose();
  217. //GC.Collect();
  218. }
  219. /// <summary>
  220. /// 获取此刻的Config数据
  221. /// </summary>
  222. /// <param name="config"></param>
  223. public void GetConfigValue(out CameraConfig Camconfig,out ShuLiConfigClass shuLiConfig)
  224. {
  225. //判断是否加载了相机
  226. if(cameraClass.IsLoadCamera())
  227. //获取已经加载的相机的配置
  228. Camconfig = cameraClass.GetConfigValue();
  229. else
  230. //新建一个相机配置
  231. Camconfig = new CameraConfig();
  232. //读取视觉配置
  233. shuLiConfig = shuLiClass.GetConfigValue();
  234. }
  235. /// <summary>
  236. /// 获取相机此刻的Config数据
  237. /// </summary>
  238. /// <param name="CameraConfig"></param>
  239. public void GetCameraConfig(out CameraConfig CameraConfig)
  240. {
  241. //判断是否加载了相机
  242. if (cameraClass.IsLoadCamera())
  243. //获取已经加载的相机的配置
  244. CameraConfig = cameraClass.GetConfigValue();
  245. else
  246. //新建一个相机配置
  247. CameraConfig = new CameraConfig();
  248. }
  249. /// <summary>
  250. /// 保存所有Config
  251. /// </summary>
  252. public void SaveAllConfig()
  253. {
  254. shuLiClass.SaveConfig();
  255. }
  256. /// <summary>
  257. /// 获取相机连接状态
  258. /// </summary>
  259. /// <returns></returns>
  260. public bool GetCameraConnectStatic()
  261. {
  262. bool result = false;
  263. if(cameraClass != null)
  264. result = cameraClass.IsConnectCamera();
  265. return result;
  266. }
  267. public void Dispose()
  268. {
  269. //MechanicalControl.Dispose();
  270. }
  271. #endregion
  272. #region 私有方法
  273. //bool IsFill = false;
  274. //bool IsXuanZhuanCloseFaMen = false;
  275. /// <summary>
  276. /// 每数完一粒识别线程执行的事件
  277. /// </summary>
  278. /// <param name="sender"></param>
  279. /// <param name="e"></param>
  280. private void Worker_OneGrainCompleted(object sender, ActiveObjectEventArgsClass e)
  281. {
  282. LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6);
  283. LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6);
  284. if (e.Actives.Where(o => o.StateCode == 7).Count() > 0)
  285. {
  286. stopwatch.Restart();
  287. _ShuLiState = false;
  288. }
  289. else if (stopwatch.ElapsedMilliseconds > 1000)
  290. {
  291. stopwatch.Stop();
  292. _ShuLiState = true;
  293. }
  294. //往数组中计数
  295. ushort result = new ushort();
  296. // 事件处理逻辑
  297. foreach (ActiveObjectClass oneActive in e.Actives)
  298. {
  299. Console.WriteLine(string.Format("输出当前颗粒信息,开始行:{0},结束行:{1},开始时间:{2}结束时间:{3},颗粒状态:{4},通道数:{5}",
  300. oneActive.StartLine, oneActive.LastSeenLine, oneActive.StartCheckTime.ToString("O"), oneActive.EndCheckTime.ToString("O"), oneActive.StateCode, oneActive.ChannelNO));
  301. LOG.log(string.Format("输出当前颗粒信息,开始行:{0},结束行:{1},开始时间:{2}结束时间:{3},颗粒状态:{4},通道数:{5}",
  302. oneActive.StartLine, oneActive.LastSeenLine, oneActive.StartCheckTime.ToString("O"), oneActive.EndCheckTime.ToString("O"), oneActive.StateCode, oneActive.ChannelNO), 6);
  303. if (oneActive.StateCode == 0 && oneActive.ChannelNO != -1)
  304. {
  305. //单通道合格计数
  306. result |= (ushort)(1 << (oneActive.ChannelNO));
  307. }
  308. else
  309. {
  310. //单通道不合格计数
  311. result |= (ushort)(1 << 8);
  312. }
  313. }
  314. LOG.log("当前待发送队列数量:" + SendQueue.Count, 6);
  315. if(IsSend)
  316. {
  317. SendQueue.Enqueue(result);
  318. }
  319. }
  320. /// <summary>
  321. /// 对外通知事件
  322. /// </summary>
  323. private void OnOneGrain(List<ActiveObjectClass> activeObject)
  324. {
  325. ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
  326. // 触发事件
  327. WorkerToFrom?.Invoke(this, activeObjectEventArgs);
  328. }
  329. #endregion
  330. #region 线程方法
  331. /// <summary>
  332. /// 开启交换线程
  333. /// </summary>
  334. private void StartSwitchThread()
  335. {
  336. IsSwitch = true;
  337. SwitchIdentifyImageThread = new Thread(SwitchIdentifyImageProcess);
  338. SwitchIdentifyImageThread.Start();
  339. }
  340. /// <summary>
  341. /// 关闭交换线程
  342. /// </summary>
  343. private void StopSwitchThread()
  344. {
  345. try
  346. {
  347. // 标志位设为false
  348. IsSwitch = false;
  349. if (SwitchIdentifyImageThread != null && SwitchIdentifyImageThread.IsAlive)
  350. SwitchIdentifyImageThread.Join();
  351. }
  352. catch (Exception ex)
  353. {
  354. LOG.error("Start thread failed!, " + ex.Message);
  355. throw;
  356. }
  357. }
  358. /// <summary>
  359. /// 交换线程
  360. /// </summary>
  361. private void SwitchIdentifyImageProcess()
  362. {
  363. IFrameOut IFramedata = null;
  364. Stopwatch stopwatch = Stopwatch.StartNew();
  365. stopwatch.Start();
  366. while (IsSwitch)
  367. {
  368. if (stopwatch.ElapsedMilliseconds > 1000)
  369. {
  370. Console.WriteLine("交换线程-图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum);
  371. GC.Collect();
  372. stopwatch.Restart();
  373. }
  374. //Thread.Sleep(5);
  375. bool result = cameraClass.GetOnceImage(out IFramedata);
  376. if (result)
  377. {
  378. if (IFramedata == null)
  379. continue;
  380. shuLiClass.SetOnceIdentifyImageData(IFramedata.Image);
  381. }
  382. else
  383. continue;
  384. IFramedata.Dispose();
  385. }
  386. stopwatch.Stop();
  387. }
  388. /// <summary>
  389. /// 启动发送消息线程
  390. /// </summary>
  391. private void StartSendBottLogicMessageThread()
  392. {
  393. try
  394. {
  395. //zmcauxClass.OpenZmcauxCard();
  396. timeBeginPeriod(1); // 设置为1ms精度
  397. IsSend = true;
  398. SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcess);
  399. SendBottLogicMessageThread.Start();
  400. }
  401. catch (Exception ex)
  402. {
  403. LOG.error("Start thread failed!, " + ex.Message);
  404. throw;
  405. }
  406. }
  407. /// <summary>
  408. /// 停止发送消息线程
  409. /// </summary>
  410. private void StopSendBottLogicMessageThread()
  411. {
  412. try
  413. {
  414. // 标志位设为false
  415. IsSend = false;
  416. if (SendBottLogicMessageThread != null && SendBottLogicMessageThread.IsAlive)
  417. SendBottLogicMessageThread.Join();
  418. if (modbusTcpClient != null) modbusTcpClient.Disconnect();
  419. }
  420. catch (Exception ex)
  421. {
  422. LOG.error("Start thread failed!, " + ex.Message);
  423. throw;
  424. }
  425. }
  426. /// <summary>
  427. /// 信息发送线程
  428. /// </summary>
  429. private void SendBottLogicMessageProcess()
  430. {
  431. //获取数据
  432. ushort sendMessage = 0;
  433. bool AllowTransfer = false;
  434. bool TransferDone = false;
  435. Stopwatch sw = Stopwatch.StartNew();
  436. while (IsSend)
  437. {
  438. LOG.log("进入线程", 6);
  439. sw.Restart();
  440. sendMessage = new ushort();
  441. //读取装瓶状态
  442. AllowTransfer = false;
  443. TransferDone = false;
  444. bool[] ReadResult = modbusTcpClient.ReadCoilsRegister(slaveId: 1, startAddress: 11, numRegisters: 2);
  445. if (ReadResult == null)
  446. {
  447. continue;
  448. }
  449. AllowTransfer = ReadResult[1];
  450. TransferDone = ReadResult[0];
  451. //LOG.log(string.Format("读取值:AllowTransfer[0]:{0},TransferDone[1]:{1}", AllowTransfer, TransferDone), 6);
  452. //当允许写入且处于未写入的状态时
  453. if (AllowTransfer && !TransferDone)
  454. {
  455. if (SendQueue.Count() > 0)
  456. {
  457. if (!SendQueue.TryDequeue(out sendMessage))
  458. {
  459. LOG.error("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!");
  460. }
  461. }
  462. if (sendMessage != 0)
  463. {
  464. //写入数据
  465. modbusTcpClient.WriteSingleRegister(slaveId: 1, registerAddress: 100, value: sendMessage);
  466. modbusTcpClient.WriteCoilsRegister(slaveId: 1, CoilsAddress: 11, values: true);
  467. }
  468. }
  469. sw.Stop();
  470. LOG.log(string.Format("离开线程写入值:sendMessage[1]:{0},此次写值耗时:{1}", sendMessage, sw.Elapsed), 6);
  471. Thread.Sleep(1);
  472. }
  473. }
  474. #endregion
  475. #region 外部函数
  476. [DllImport("winmm.dll")]
  477. static extern uint timeBeginPeriod(uint period);
  478. #endregion
  479. }
  480. }